/*
===========================================================================

Wolfenstein: Enemy Territory GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 

This file is part of the Wolfenstein: Enemy Territory GPL Source Code (Wolf ET Source Code).  

Wolf ET Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Wolf ET Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Wolf ET Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/



// bg_pmove.c -- both games player movement code
// takes a playerstate and a usercmd as input and returns a modifed playerstate


#ifdef CGAMEDLL
#include "../cgame/cg_local.h"
#else
#include "q_shared.h"
#include "bg_public.h"
#endif // CGAMEDLL

#include "bg_local.h"

#ifdef CGAMEDLL
#define PM_GameType cg_gameType.integer
#elif GAMEDLL
#define PM_GameType g_gametype.integer
#endif

#define PM_IsSinglePlayerGame() ( PM_GameType == GT_SINGLE_PLAYER || PM_GameType == GT_COOP )


// JPW NERVE -- stuck this here so it can be seen client & server side
float Com_GetFlamethrowerRange( void ) {
	return 2500; // multiplayer range is longer for balance
}
// jpw

pmove_t     *pm;
pml_t pml;

// movement parameters
float pm_stopspeed = 100;
//float	pm_duckScale = 0.25;

//----(SA)	modified
float pm_waterSwimScale   = 0.5;
float pm_waterWadeScale   = 0.70;
float pm_slagSwimScale    = 0.30;
float pm_slagWadeScale    = 0.70;

float pm_proneSpeedScale  = 0.21;   // was: 0.18 (too slow) then: 0.24 (too fast)

float pm_accelerate       = 10;
float pm_airaccelerate    = 1;
float pm_wateraccelerate  = 4;
float pm_slagaccelerate   = 2;
float pm_flyaccelerate    = 8;

float pm_friction         = 6;
float pm_waterfriction    = 1;
float pm_slagfriction     = 1;
float pm_flightfriction   = 3;
float pm_ladderfriction   = 14;
float pm_spectatorfriction = 5.0f;

//----(SA)	end

int c_pmove = 0;

#define TRIPMINE_RANGE 512.f


#ifdef GAMEDLL

// In just the GAME DLL, we want to store the groundtrace surface stuff,
// so we don't have to keep tracing.
void ClientStoreSurfaceFlags( int clientNum, int surfaceFlags );

#endif


/*
===============
PM_AddEvent

===============
*/
void PM_AddEvent( int newEvent ) {
	BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
}

void PM_AddEventExt( int newEvent, int eventParm ) {
	BG_AddPredictableEventToPlayerstate( newEvent, eventParm, pm->ps );
}

int PM_IdleAnimForWeapon( int weapon ) {
	switch ( weapon ) {
	case WP_GPG40:
	case WP_M7:
	case WP_SATCHEL_DET:
	case WP_MORTAR_SET:
	case WP_MEDIC_ADRENALINE:
	case WP_MOBILE_MG42_SET:
		return WEAP_IDLE2;

	default:
		return WEAP_IDLE1;
	}
}

int PM_AltSwitchFromForWeapon( int weapon ) {
	switch ( weapon ) {
//		case WP_MEDIC_SYRINGE:
//			return WEAP_DROP;
	default:
		return WEAP_ALTSWITCHFROM;
	}
}

int PM_AltSwitchToForWeapon( int weapon ) {
	switch ( weapon ) {
	case WP_GPG40:
	case WP_M7:
	case WP_MORTAR:
	case WP_MOBILE_MG42:
		return WEAP_ALTSWITCHFROM;
//		case WP_MEDIC_SYRINGE:
//			return WEAP_RAISE;

	default:
		return WEAP_ALTSWITCHTO;
	}
}

int PM_AttackAnimForWeapon( int weapon ) {
	switch ( weapon ) {
	case WP_GPG40:
	case WP_M7:
	case WP_SATCHEL_DET:
	case WP_MEDIC_ADRENALINE:
	case WP_MOBILE_MG42_SET:
		return WEAP_ATTACK2;

	default:
		return WEAP_ATTACK1;
	}
}

int PM_LastAttackAnimForWeapon( int weapon ) {
	switch ( weapon ) {
	case WP_GPG40:
	case WP_M7:
	case WP_MOBILE_MG42_SET:
		return WEAP_ATTACK2;
	case WP_MORTAR_SET:
		return WEAP_ATTACK1;

	default:
		return WEAP_ATTACK_LASTSHOT;
	}
}

int PM_ReloadAnimForWeapon( int weapon ) {
	switch ( weapon ) {
	case WP_GPG40:
	case WP_M7:
		return WEAP_RELOAD2;
	case WP_MOBILE_MG42_SET:
		return WEAP_RELOAD3;
	default:
		if ( pm->skill[SK_LIGHT_WEAPONS] >= 2 && BG_isLightWeaponSupportingFastReload( weapon )  ) {
			return WEAP_RELOAD2;        // faster reload
		} else {
			return WEAP_RELOAD1;
		}
	}
}

int PM_RaiseAnimForWeapon( int weapon ) {
	switch ( weapon ) {
	case WP_GPG40:
	case WP_M7:
		return WEAP_RELOAD3;
	case WP_MOBILE_MG42_SET:
		return WEAP_DROP2;
	case WP_SATCHEL_DET:
		return WEAP_RELOAD2;

	default:
		return WEAP_RAISE;
	}
}

int PM_DropAnimForWeapon( int weapon ) {
	switch ( weapon ) {
	case WP_GPG40:
	case WP_M7:
		return WEAP_DROP2;
	case WP_SATCHEL_DET:
		return WEAP_RELOAD1;

	default:
		return WEAP_DROP;
	}
}

/*
===============
PM_AddTouchEnt
===============
*/
void PM_AddTouchEnt( int entityNum ) {
	int i;

	if ( entityNum == ENTITYNUM_WORLD ) {
		return;
	}
	if ( pm->numtouch == MAXTOUCH ) {
		return;
	}

	// see if it is already added
	for ( i = 0 ; i < pm->numtouch ; i++ ) {
		if ( pm->touchents[ i ] == entityNum ) {
			return;
		}
	}

	// add it
	pm->touchents[pm->numtouch] = entityNum;
	pm->numtouch++;
}

/*
==============
PM_StartWeaponAnim
==============
*/
static void PM_StartWeaponAnim( int anim ) {
	if ( pm->ps->pm_type >= PM_DEAD ) {
		return;
	}

	if ( pm->pmext->weapAnimTimer > 0 ) {
		return;
	}

	if ( pm->cmd.weapon == WP_NONE ) {
		return;
	}

	pm->ps->weapAnim = ( ( pm->ps->weapAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
}

void PM_ContinueWeaponAnim( int anim ) {
	if ( pm->cmd.weapon == WP_NONE ) {
		return;
	}

	if ( ( pm->ps->weapAnim & ~ANIM_TOGGLEBIT ) == anim ) {
		return;
	}
	if ( pm->pmext->weapAnimTimer > 0 ) {
		return;     // a high priority animation is running
	}
	PM_StartWeaponAnim( anim );
}

/*
==================
PM_ClipVelocity

Slide off of the impacting surface
==================
*/
void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
	float backoff;
	float change;
	int i;

	backoff = DotProduct( in, normal );

	if ( backoff < 0 ) {
		backoff *= overbounce;
	} else {
		backoff /= overbounce;
	}

	for ( i = 0 ; i < 3 ; i++ ) {
		change = normal[i] * backoff;
		out[i] = in[i] - change;
	}
}

/*
==================
PM_TraceAll

finds worst trace of body/legs, for collision.
==================
*/

void PM_TraceLegs( trace_t *trace, float *legsOffset, vec3_t start, vec3_t end, trace_t *bodytrace, vec3_t viewangles, void( tracefunc ) ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask ), int ignoreent, int tracemask ) {
	trace_t steptrace;
	vec3_t ofs, org, point;
	vec3_t flatforward;
	float angle;

	// zinx - don't let players block legs
	tracemask &= ~( CONTENTS_BODY | CONTENTS_CORPSE );

	if ( legsOffset ) {
		*legsOffset = 0;
	}

	angle = DEG2RAD( viewangles[YAW] );
	flatforward[0] = cos( angle );
	flatforward[1] = sin( angle );
	flatforward[2] = 0;

	VectorScale( flatforward, -32, ofs );

	VectorAdd( start, ofs, org );
	VectorAdd( end, ofs, point );
	tracefunc( trace, org, playerlegsProneMins, playerlegsProneMaxs, point, ignoreent, tracemask );
	if ( !bodytrace || trace->fraction < bodytrace->fraction ||
		 trace->allsolid ) {
		// legs are clipping sooner than body
		// see if our legs can step up

		// give it a try with the new height
		ofs[2] += STEPSIZE;

		VectorAdd( start, ofs, org );
		VectorAdd( end, ofs, point );
		tracefunc( &steptrace, org, playerlegsProneMins, playerlegsProneMaxs, point, ignoreent, tracemask );
		if ( !steptrace.allsolid && !steptrace.startsolid &&
			 steptrace.fraction > trace->fraction ) {
			// the step trace did better -- use it instead
			*trace = steptrace;

			// get legs offset
			if ( legsOffset ) {
				*legsOffset = ofs[2];

				VectorCopy( steptrace.endpos, org );
				VectorCopy( steptrace.endpos, point );
				point[2] -= STEPSIZE;

				tracefunc( &steptrace, org, playerlegsProneMins, playerlegsProneMaxs, point, ignoreent, tracemask );
				if ( !steptrace.allsolid ) {
					*legsOffset = ofs[2] - ( org[2] - steptrace.endpos[2] );
				}
			}
		}
	}
}

/* Traces all player bboxes -- body and legs */
void    PM_TraceAllLegs( trace_t *trace, float *legsOffset, vec3_t start, vec3_t end ) {
	pm->trace( trace, start, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask );

	/* legs */
	if ( pm->ps->eFlags & EF_PRONE ) {
		trace_t legtrace;

		PM_TraceLegs( &legtrace, legsOffset, start, end, trace, pm->ps->viewangles, pm->trace, pm->ps->clientNum, pm->tracemask );

		if ( legtrace.fraction < trace->fraction ||
			 legtrace.startsolid ||
			 legtrace.allsolid ) {
			VectorSubtract( end, start, legtrace.endpos );
			VectorMA( start, legtrace.fraction, legtrace.endpos, legtrace.endpos );
			*trace = legtrace;
		}
	}
}

void PM_TraceAll( trace_t *trace, vec3_t start, vec3_t end ) {
	PM_TraceAllLegs( trace, NULL, start, end );
}

/*
========================
PM_ExertSound

plays random exertion sound when sprint key is press
========================
*/
/*static void PM_ExertSound (void)
{
	int rval;
	static int	oldexerttime = 0;
	static int	oldexertcnt = 0;

	if (pm->cmd.serverTime > oldexerttime + 500)
		oldexerttime = pm->cmd.serverTime;
	else
		return;

	rval = rand()%3;

	if (oldexertcnt != rval)
		 oldexertcnt = rval;
	else
		oldexertcnt++;

	if (oldexertcnt > 2)
		oldexertcnt = 0;

	if (oldexertcnt == 1)
		PM_AddEvent (EV_EXERT2);
	else if (oldexertcnt == 2)
		PM_AddEvent (EV_EXERT3);
	else
		PM_AddEvent (EV_EXERT1);
}*/


/*
==================
PM_Friction

Handles both ground friction and water friction
==================
*/
static void PM_Friction( void ) {
	vec3_t vec;
	float   *vel;
	float speed, newspeed, control;
	float drop;

	vel = pm->ps->velocity;

	VectorCopy( vel, vec );
	if ( pml.walking ) {
		vec[2] = 0; // ignore slope movement
	}

	speed = VectorLength( vec );
	// rain - #179 don't do this for PM_SPECTATOR/PM_NOCLIP, we always want them to stop
	if ( speed < 1 && pm->ps->pm_type != PM_SPECTATOR && pm->ps->pm_type != PM_NOCLIP ) {
		vel[0] = 0;
		vel[1] = 0;     // allow sinking underwater
		// FIXME: still have z friction underwater?
		return;
	}

	drop = 0;

	// apply end of dodge friction
	if ( pm->cmd.serverTime - pm->pmext->dodgeTime < 350 &&
		 pm->cmd.serverTime - pm->pmext->dodgeTime > 250 ) {
		drop += speed * 20 * pml.frametime;
	}

	// apply ground friction
	if ( pm->waterlevel <= 1 ) {
		if ( pml.walking && !( pml.groundTrace.surfaceFlags & SURF_SLICK ) ) {
			// if getting knocked back, no friction
			if ( !( pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) ) {
				control = speed < pm_stopspeed ? pm_stopspeed : speed;
				drop += control * pm_friction * pml.frametime;
			}
		}
	}

	// apply water friction even if just wading
	if ( pm->waterlevel ) {
		if ( pm->watertype == CONTENTS_SLIME ) { //----(SA)	slag
			drop += speed * pm_slagfriction * pm->waterlevel * pml.frametime;
		} else {
			drop += speed * pm_waterfriction * pm->waterlevel * pml.frametime;
		}
	}

	if ( pm->ps->pm_type == PM_SPECTATOR ) {
		drop += speed * pm_spectatorfriction * pml.frametime;
	}

	// apply ladder strafe friction
	if ( pml.ladder ) {
		drop += speed * pm_ladderfriction * pml.frametime;
	}

	// scale the velocity
	newspeed = speed - drop;
	if ( newspeed < 0 ) {
		newspeed = 0;
	}
	newspeed /= speed;

	// rain - if we're barely moving and barely slowing down, we want to
	// help things along--we don't want to end up getting snapped back to
	// our previous speed
	if ( pm->ps->pm_type == PM_SPECTATOR || pm->ps->pm_type == PM_NOCLIP ) {
		if ( drop < 1.0f && speed < 3.0f ) {
			newspeed = 0.0;
		}
	}

	// rain - used VectorScale instead of multiplying by hand
	VectorScale( vel, newspeed, vel );
}


/*
==============
PM_Accelerate

Handles user intended acceleration
==============
*/
static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) {
#if 1
	// q2 style
	int i;
	float addspeed, accelspeed, currentspeed;

	currentspeed = DotProduct( pm->ps->velocity, wishdir );
	addspeed = wishspeed - currentspeed;
	if ( addspeed <= 0 ) {
		return;
	}
	accelspeed = accel * pml.frametime * wishspeed;
	if ( accelspeed > addspeed ) {
		accelspeed = addspeed;
	}

	// Ridah, variable friction for AI's
	if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) {
		accelspeed *= ( 1.0 / pm->ps->friction );
	}
	if ( accelspeed > addspeed ) {
		accelspeed = addspeed;
	}

	for ( i = 0 ; i < 3 ; i++ ) {
		pm->ps->velocity[i] += accelspeed * wishdir[i];
	}
#else
	// proper way (avoids strafe jump maxspeed bug), but feels bad
	vec3_t wishVelocity;
	vec3_t pushDir;
	float pushLen;
	float canPush;

	VectorScale( wishdir, wishspeed, wishVelocity );
	VectorSubtract( wishVelocity, pm->ps->velocity, pushDir );
	pushLen = VectorNormalize( pushDir );

	canPush = accel * pml.frametime * wishspeed;
	if ( canPush > pushLen ) {
		canPush = pushLen;
	}

	VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity );
#endif
}



// JPW NERVE -- added because I need to check single/multiplayer instances and branch accordingly
#ifdef CGAMEDLL
extern vmCvar_t cg_gameType;
extern vmCvar_t cg_movespeed;
#endif
#ifdef GAMEDLL
extern vmCvar_t g_gametype;
extern vmCvar_t g_movespeed;
#endif

/*
============
PM_CmdScale

Returns the scale factor to apply to cmd movements
This allows the clients to use axial -127 to 127 values for all directions
without getting a sqrt(2) distortion in speed.
============
*/
static float PM_CmdScale( usercmd_t *cmd ) {
	int max;
	float total;
	float scale;

#ifdef CGAMEDLL
	int gametype = cg_gameType.integer;
	int movespeed = cg_movespeed.integer;
#elif GAMEDLL
	int gametype = g_gametype.integer;
	int movespeed = g_movespeed.integer;
#endif

	max = abs( cmd->forwardmove );
	if ( abs( cmd->rightmove ) > max ) {
		max = abs( cmd->rightmove );
	}
	if ( abs( cmd->upmove ) > max ) {
		max = abs( cmd->upmove );
	}
	if ( !max ) {
		return 0;
	}

	total = sqrt( cmd->forwardmove * cmd->forwardmove
				  + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove );
	scale = (float)pm->ps->speed * max / ( 127.0 * total );

	if ( pm->cmd.buttons & BUTTON_SPRINT && pm->pmext->sprintTime > 50 ) {
		scale *= pm->ps->sprintSpeedScale;
	} else {
		scale *= pm->ps->runSpeedScale;
	}

	if ( pm->ps->pm_type == PM_NOCLIP ) {
		scale *= 3;
	}

// JPW NERVE -- half move speed if heavy weapon is carried
// this is the counterstrike way of doing it -- ie you can switch to a non-heavy weapon and move at
// full speed.  not completely realistic (well, sure, you can run faster with the weapon strapped to your
// back than in carry position) but more fun to play.  If it doesn't play well this way we'll bog down the
// player if the own the weapon at all.
//
	if ( ( pm->ps->weapon == WP_PANZERFAUST ) ||
		 ( pm->ps->weapon == WP_MOBILE_MG42 ) ||
		 ( pm->ps->weapon == WP_MOBILE_MG42_SET ) ||
		 ( pm->ps->weapon == WP_MORTAR ) ) {
		if ( pm->skill[SK_HEAVY_WEAPONS] >= 3 ) {
			scale *= 0.75;
		} else {
			scale *= 0.5;
		}
	}

	if ( pm->ps->weapon == WP_FLAMETHROWER ) { // trying some different balance for the FT
		if ( !( pm->skill[SK_HEAVY_WEAPONS] >= 3 ) || pm->cmd.buttons & BUTTON_ATTACK ) {
			scale *= 0.7;
		}
	}


	if ( gametype == GT_SINGLE_PLAYER || gametype == GT_COOP ) {
		// Adjust the movespeed
		scale *= ( ( (float) movespeed ) / (float) 127 );

	} // if (gametype == GT_SINGLE_PLAYER)...

	return scale;
}


/*
================
PM_SetMovementDir

Determine the rotation of the legs reletive
to the facing dir
================
*/
static void PM_SetMovementDir( void ) {
// Ridah, changed this for more realistic angles (at the cost of more network traffic?)
#if 1
	float speed;
	vec3_t moved;
	int moveyaw;

	VectorSubtract( pm->ps->origin, pml.previous_origin, moved );

	if (    ( pm->cmd.forwardmove || pm->cmd.rightmove )
			&&  ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
			&&  ( speed = VectorLength( moved ) )
			&&  ( speed > pml.frametime * 5 ) ) { // if moving slower than 20 units per second, just face head angles
		vec3_t dir;

		VectorNormalize2( moved, dir );
		vectoangles( dir, dir );

		moveyaw = (int)AngleDelta( dir[YAW], pm->ps->viewangles[YAW] );

		if ( pm->cmd.forwardmove < 0 ) {
			moveyaw = (int)AngleNormalize180( moveyaw + 180 );
		}

		if ( abs( moveyaw ) > 75 ) {
			if ( moveyaw > 0 ) {
				moveyaw = 75;
			} else
			{
				moveyaw = -75;
			}
		}

		pm->ps->movementDir = (signed char)moveyaw;
	} else
	{
		pm->ps->movementDir = 0;
	}
#else
	if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
		if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
			pm->ps->movementDir = 0;
		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
			pm->ps->movementDir = 1;
		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
			pm->ps->movementDir = 2;
		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
			pm->ps->movementDir = 3;
		} else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
			pm->ps->movementDir = 4;
		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
			pm->ps->movementDir = 5;
		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
			pm->ps->movementDir = 6;
		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
			pm->ps->movementDir = 7;
		}
	} else {
		// if they aren't actively going directly sideways,
		// change the animation to the diagonal so they
		// don't stop too crooked
		if ( pm->ps->movementDir == 2 ) {
			pm->ps->movementDir = 1;
		} else if ( pm->ps->movementDir == 6 ) {
			pm->ps->movementDir = 7;
		}
	}
#endif
}


/*
=============
PM_CheckJump
=============
*/
static qboolean PM_CheckJump( void ) {
	// no jumpin when prone
	if ( pm->ps->eFlags & EF_PRONE ) {
		return qfalse;
	}

	// JPW NERVE -- jumping in multiplayer uses and requires sprint juice (to prevent turbo skating, sprint + jumps)
	// don't allow jump accel

	// rain - revert to using pmext for this since pmext is fixed now.
	// fix for #166
	if ( pm->cmd.serverTime - pm->pmext->jumpTime < 850 ) {
		return qfalse;
	}

	// don't allow if player tired
//	if (pm->pmext->sprintTime < 2500) // JPW pulled this per id request; made airborne jumpers wildly inaccurate with gunfire to compensate
//		return qfalse;
	// jpw

	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
		return qfalse;      // don't allow jump until all buttons are up
	}

	if ( pm->cmd.upmove < 10 ) {
		// not holding jump
		return qfalse;
	}

	// must wait for jump to be released
	if ( pm->ps->pm_flags & PMF_JUMP_HELD ) {
		// clear upmove so cmdscale doesn't lower running speed
		pm->cmd.upmove = 0;
		return qfalse;
	}

	pml.groundPlane = qfalse;       // jumping away
	pml.walking = qfalse;
	pm->ps->pm_flags |= PMF_JUMP_HELD;

	pm->ps->groundEntityNum = ENTITYNUM_NONE;
	pm->ps->velocity[2] = JUMP_VELOCITY;
	PM_AddEvent( EV_JUMP );

	if ( pm->cmd.forwardmove >= 0 ) {
		BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_JUMP, qfalse, qtrue );
		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
	} else {
		BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_JUMPBK, qfalse, qtrue );
		pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
	}

	return qtrue;
}

/*
=============
PM_CheckWaterJump
=============
*/
static qboolean PM_CheckWaterJump( void ) {
	vec3_t spot;
	int cont;
	vec3_t flatforward;

	if ( pm->ps->pm_time ) {
		return qfalse;
	}

	// check for water jump
	if ( pm->waterlevel != 2 ) {
		return qfalse;
	}

	flatforward[0] = pml.forward[0];
	flatforward[1] = pml.forward[1];
	flatforward[2] = 0;
	VectorNormalize( flatforward );

	VectorMA( pm->ps->origin, 30, flatforward, spot );
	spot[2] += 4;
	cont = pm->pointcontents( spot, pm->ps->clientNum );
	if ( !( cont & CONTENTS_SOLID ) ) {
		return qfalse;
	}

	spot[2] += 16;
	cont = pm->pointcontents( spot, pm->ps->clientNum );
	if ( cont ) {
		return qfalse;
	}

	// jump out of water
	VectorScale( pml.forward, 200, pm->ps->velocity );
	pm->ps->velocity[2] = 350;

	pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
	pm->ps->pm_time = 2000;

	return qtrue;
}

/*
==============
PM_CheckProne

Sets mins, maxs, and pm->ps->viewheight
==============
*/
static qboolean PM_CheckProne( void ) {
	//Com_Printf( "%i: PM_CheckProne (%i)\n", pm->cmd.serverTime, pm->pmext->proneGroundTime );

	if ( !( pm->ps->eFlags & EF_PRONE ) ) {
		// needs to be on the ground
//		if( !pml.walking ) {
//			return qfalse;
//		}

		// can't go prone on ladders
		if ( pm->ps->pm_flags & PMF_LADDER ) {
			return qfalse;
		}

		// no prone when using mg42's
		if ( pm->ps->persistant[PERS_HWEAPON_USE] || pm->ps->eFlags & EF_MOUNTEDTANK ) {
			return qfalse;
		}

		if ( pm->ps->weaponDelay && pm->ps->weapon == WP_PANZERFAUST ) {
			return qfalse;
		}

		if ( pm->ps->weapon == WP_MORTAR_SET ) {
			return qfalse;
		}

		// can't go prone while swimming
		if ( pm->waterlevel > 1 ) {
			return qfalse;
		}

		// can't go prone when fiddling with mg42
		//if( pm->ps->weaponstate == WEAPON_FROMPRONE ) {
		//	return qfalse;
		//}

		if ( ( ( pm->ps->pm_flags & PMF_DUCKED && pm->cmd.doubleTap == DT_FORWARD ) ||
			   ( pm->cmd.wbuttons & WBUTTON_PRONE ) ) && pm->cmd.serverTime - -pm->pmext->proneTime > 750  ) {
			trace_t trace;

			pm->mins[0] = pm->ps->mins[0];
			pm->mins[1] = pm->ps->mins[1];

			pm->maxs[0] = pm->ps->maxs[0];
			pm->maxs[1] = pm->ps->maxs[1];

			pm->mins[2] = pm->ps->mins[2];
			pm->maxs[2] = pm->ps->crouchMaxZ;

			pm->ps->eFlags |= EF_PRONE;
			PM_TraceAll( &trace, pm->ps->origin, pm->ps->origin );
			pm->ps->eFlags &= ~EF_PRONE;

			if ( !trace.startsolid && !trace.allsolid ) {
				// go prone
				pm->ps->pm_flags |= PMF_DUCKED; // crouched as well
				pm->ps->eFlags |= EF_PRONE;
				pm->pmext->proneTime = pm->cmd.serverTime;  // timestamp 'go prone'
				pm->pmext->proneGroundTime = pm->cmd.serverTime;
			}
		}
	}

	if ( pm->ps->eFlags & EF_PRONE ) {
		if ( pm->waterlevel > 1 ||
			 pm->ps->pm_type == PM_DEAD ||
			 pm->ps->eFlags & EF_MOUNTEDTANK ||
// zinx - what was the reason for this, anyway? removing fixes bug 424
//			pm->cmd.serverTime - pm->pmext->proneGroundTime > 450 ||
			 ( ( pm->cmd.doubleTap == DT_BACK || pm->cmd.upmove > 10 || pm->cmd.wbuttons & WBUTTON_PRONE ) && pm->cmd.serverTime - pm->pmext->proneTime > 750 ) ) {
			trace_t trace;

			// see if we have the space to stop prone
			pm->mins[0] = pm->ps->mins[0];
			pm->mins[1] = pm->ps->mins[1];

			pm->maxs[0] = pm->ps->maxs[0];
			pm->maxs[1] = pm->ps->maxs[1];

			pm->mins[2] = pm->ps->mins[2];
			pm->maxs[2] = pm->ps->crouchMaxZ;

			pm->ps->eFlags &= ~EF_PRONE;
			PM_TraceAll( &trace, pm->ps->origin, pm->ps->origin );
			pm->ps->eFlags |= EF_PRONE;

			if ( !trace.allsolid ) {
				// crouch for a bit
				pm->ps->pm_flags |= PMF_DUCKED;

				// stop prone
				pm->ps->eFlags &= ~EF_PRONE;
				pm->ps->eFlags &= ~EF_PRONE_MOVING;
				pm->pmext->proneTime = -pm->cmd.serverTime; // timestamp 'stop prone'

				if ( pm->ps->weapon == WP_MOBILE_MG42_SET ) {
					PM_BeginWeaponChange( WP_MOBILE_MG42_SET, WP_MOBILE_MG42, qfalse );
				}

				// don't jump for a bit
				pm->pmext->jumpTime = pm->cmd.serverTime - 650;
				pm->ps->jumpTime = pm->cmd.serverTime - 650;
			}
		}
	}

	if ( pm->ps->eFlags & EF_PRONE ) {
		//float frac;

		// See if we are moving
		float spd = VectorLength( pm->ps->velocity );
		qboolean userinput = abs( pm->cmd.forwardmove ) + abs( pm->cmd.rightmove ) > 10 ? qtrue : qfalse;

		if ( userinput && spd > 40.f && !( pm->ps->eFlags & EF_PRONE_MOVING ) ) {
			pm->ps->eFlags |= EF_PRONE_MOVING;

			switch ( pm->ps->weapon ) {
			case WP_FG42SCOPE: PM_BeginWeaponChange( WP_FG42SCOPE, WP_FG42, qfalse ); break;
			case WP_GARAND_SCOPE: PM_BeginWeaponChange( WP_GARAND_SCOPE, WP_GARAND, qfalse ); break;
			case WP_K43_SCOPE: PM_BeginWeaponChange( WP_K43_SCOPE, WP_K43, qfalse ); break;
			}
		} else if ( !userinput && spd < 20.0f && ( pm->ps->eFlags & EF_PRONE_MOVING ) ) {
			pm->ps->eFlags &= ~EF_PRONE_MOVING;
		}

		pm->mins[0] = pm->ps->mins[0];
		pm->mins[1] = pm->ps->mins[1];

		pm->maxs[0] = pm->ps->maxs[0];
		pm->maxs[1] = pm->ps->maxs[1];

		pm->mins[2] = pm->ps->mins[2];

		//frac = (pm->cmd.serverTime - pm->pmext->proneTime) / 500.f;
		//if( frac > 1.f )
		//	frac = 1.f;

		//pm->maxs[2] = pm->ps->maxs[2] - (frac * (pm->ps->standViewHeight - PRONE_VIEWHEIGHT));
		//pm->ps->viewheight = DEFAULT_VIEWHEIGHT - (frac * (DEFAULT_VIEWHEIGHT - PRONE_VIEWHEIGHT));	// default - prone to get a positive which is subtracted from default
		pm->maxs[2] = pm->ps->maxs[2] - pm->ps->standViewHeight - PRONE_VIEWHEIGHT;
		pm->ps->viewheight = PRONE_VIEWHEIGHT;

		return( qtrue );
	}

	return( qfalse );
}

/*
=============
PM_CheckDodge
=============
*/
static qboolean PM_CheckDodge( void ) {
	// JPW NERVE -- jumping in multiplayer uses and requires sprint juice (to prevent turbo skating, sprint + jumps)
	// don't allow jump accel
	//if (pm->cmd.serverTime - pm->pmext->jumpTime < 850)
	//	return qfalse;

	// don't allow if player tired
//		if (pm->pmext->sprintTime < 2500) // JPW pulled this per id request; made airborne jumpers wildly inaccurate with gunfire to compensate
//			return qfalse;
	// jpw

	// Disabled for now
	return qfalse;

/*	if ( pm->pmext->sprintTime < 3000 )
		return qfalse;

	if( pm->cmd.serverTime - pm->pmext->dodgeTime < 350 )
		return qtrue;   // Already dodging

	if ( pm->ps->pm_flags & PMF_RESPAWNED || pm->ps->pm_flags & PMF_DUCKED || pm->ps->eFlags & EF_PRONE ) {
		return qfalse;		// don't allow dodge until all buttons are up, player is prone or crouching
	}

	if ( pm->cmd.doubleTap != DT_MOVELEFT && pm->cmd.doubleTap != DT_MOVERIGHT ) {
		// no dodge issued
		return qfalse;
	}

	// must wait for jump to be released
	//if ( pm->ps->pm_flags & PMF_JUMP_HELD ) {
	//	// clear upmove so cmdscale doesn't lower running speed
	//	pm->cmd.upmove = 0;
	//	return qfalse;
	//}

	pml.groundPlane = qfalse;		// jumping away
	pml.walking = qfalse;
	//pm->ps->pm_flags |= PMF_JUMP_HELD;

	pm->ps->groundEntityNum = ENTITYNUM_NONE;
	pm->ps->velocity[2] = 130;
	PM_AddEvent( EV_JUMP );

	//if ( pm->cmd.forwardmove >= 0 ) {
	//	BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMP, qfalse, qtrue );
	//	pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
	//} else {
	//	BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMPBK, qfalse, qtrue );
	//	pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
	//}

	pm->pmext->dtmove = pm->cmd.doubleTap;
	pm->pmext->dodgeTime = pm->cmd.serverTime;
	pm->pmext->jumpTime = pm->cmd.serverTime - 200;	// Arnout: prevent bunnyhopping
	pm->ps->jumpTime = pm->cmd.serverTime - 200;	// Arnout: prevent bunnyhopping

	pm->pmext->sprintTime -= 3000;

	return qtrue;*/
}

//============================================================================

/*
===================
PM_WaterJumpMove

Flying out of the water
===================
*/
static void PM_WaterJumpMove( void ) {
	// waterjump has no control, but falls

	PM_StepSlideMove( qtrue );

	pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
	if ( pm->ps->velocity[2] < 0 ) {
		// cancel as soon as we are falling down again
		pm->ps->pm_flags &= ~PMF_ALL_TIMES;
		pm->ps->pm_time = 0;
	}
}

/*
===================
PM_WaterMove

===================
*/
static void PM_WaterMove( void ) {
	int i;
	vec3_t wishvel;
	float wishspeed;
	vec3_t wishdir;
	float scale;
	float vel;

	if ( PM_CheckWaterJump() ) {
		PM_WaterJumpMove();
		return;
	}
#if 0
	// jump = head for surface
	if ( pm->cmd.upmove >= 10 ) {
		if ( pm->ps->velocity[2] > -300 ) {
			if ( pm->watertype == CONTENTS_WATER ) {
				pm->ps->velocity[2] = 100;
			} else if ( pm->watertype == CONTENTS_SLIME ) {
				pm->ps->velocity[2] = 80;
			} else {
				pm->ps->velocity[2] = 50;
			}
		}
	}
#endif
	PM_Friction();

	scale = PM_CmdScale( &pm->cmd );
	//
	// user intentions
	//
	if ( !scale ) {
		wishvel[0] = 0;
		wishvel[1] = 0;
		wishvel[2] = -60;       // sink towards bottom
//		wishvel[2] = -10;	//----(SA)	mod for DM
	} else {
		for ( i = 0 ; i < 3 ; i++ )
			wishvel[i] = scale * pml.forward[i] * pm->cmd.forwardmove + scale * pml.right[i] * pm->cmd.rightmove;

		wishvel[2] += scale * pm->cmd.upmove;
	}

	VectorCopy( wishvel, wishdir );
	wishspeed = VectorNormalize( wishdir );

	if ( pm->watertype == CONTENTS_SLIME ) {    //----(SA)	slag
		if ( wishspeed > pm->ps->speed * pm_slagSwimScale ) {
			wishspeed = pm->ps->speed * pm_slagSwimScale;
		}

		PM_Accelerate( wishdir, wishspeed, pm_slagaccelerate );
	} else {
		if ( wishspeed > pm->ps->speed * pm_waterSwimScale ) {
			wishspeed = pm->ps->speed * pm_waterSwimScale;
		}

		PM_Accelerate( wishdir, wishspeed, pm_wateraccelerate );
	}


	// make sure we can go up slopes easily under water
	if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
		vel = VectorLength( pm->ps->velocity );
		// slide along the ground plane
		PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal,
						 pm->ps->velocity, OVERCLIP );

		VectorNormalize( pm->ps->velocity );
		VectorScale( pm->ps->velocity, vel, pm->ps->velocity );
	}

	PM_SlideMove( qfalse );
}

// TTimo gcc: defined but not used
#if 0
/*
===================
PM_InvulnerabilityMove

Only with the invulnerability powerup
===================
*/
static void PM_InvulnerabilityMove( void ) {
	pm->cmd.forwardmove = 0;
	pm->cmd.rightmove = 0;
	pm->cmd.upmove = 0;
	VectorClear( pm->ps->velocity );
}
#endif

/*
===================
PM_FlyMove

Only with the flight powerup
===================
*/
static void PM_FlyMove( void ) {
	int i;
	vec3_t wishvel;
	float wishspeed;
	vec3_t wishdir;
	float scale;

	// normal slowdown
	PM_Friction();

	scale = PM_CmdScale( &pm->cmd );

	//
	// user intentions
	//
	if ( !scale ) {
		wishvel[0] = 0;
		wishvel[1] = 0;
		wishvel[2] = 0;
	} else {
		for ( i = 0 ; i < 3 ; i++ ) {
			wishvel[i] = scale * pml.forward[i] * pm->cmd.forwardmove + scale * pml.right[i] * pm->cmd.rightmove;
		}

		wishvel[2] += scale * pm->cmd.upmove;
	}

	VectorCopy( wishvel, wishdir );
	wishspeed = VectorNormalize( wishdir );

	PM_Accelerate( wishdir, wishspeed, pm_flyaccelerate );

	PM_StepSlideMove( qfalse );
}


/*
===================
PM_AirMove

===================
*/
static void PM_AirMove( void ) {
	int i;
	vec3_t wishvel;
	float fmove, smove;
	vec3_t wishdir;
	float wishspeed;
	float scale;
	usercmd_t cmd;

	PM_Friction();

	fmove = pm->cmd.forwardmove;
	smove = pm->cmd.rightmove;

	// project moves down to flat plane
	if ( pm->cmd.serverTime - pm->pmext->dodgeTime < 350 ) {
		pml.forward[2] = fmove = 0;
		smove = pm->pmext->dtmove == DT_MOVELEFT ? -2070 : 2070;
		scale = 1.f;
	} else {
		cmd = pm->cmd;
		scale = PM_CmdScale( &cmd );

		// Ridah, moved this down, so we use the actual movement direction
		// set the movementDir so clients can rotate the legs for strafing
		//	PM_SetMovementDir();


		pml.forward[2] = 0;
		pml.right[2] = 0;
	}
	VectorNormalize( pml.forward );
	VectorNormalize( pml.right );

	for ( i = 0 ; i < 2 ; i++ ) {
		wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove;
	}
	wishvel[2] = 0;

	VectorCopy( wishvel, wishdir );
	wishspeed = VectorNormalize( wishdir );
	wishspeed *= scale;

	// not on ground, so little effect on velocity
	PM_Accelerate( wishdir, wishspeed, pm_airaccelerate );

	// we may have a ground plane that is very steep, even
	// though we don't have a groundentity
	// slide along the steep plane
	if ( pml.groundPlane ) {
		PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal,
						 pm->ps->velocity, OVERCLIP );
	}

#if 0
	//ZOID:  If we are on the grapple, try stair-stepping
	//this allows a player to use the grapple to pull himself
	//over a ledge
	if ( pm->ps->pm_flags & PMF_GRAPPLE_PULL ) {
		PM_StepSlideMove( qtrue );
	} else {
		PM_SlideMove( qtrue );
	}
#endif

	PM_StepSlideMove( qtrue );

// Ridah, moved this down, so we use the actual movement direction
	// set the movementDir so clients can rotate the legs for strafing
	PM_SetMovementDir();
}



/*
===================
PM_WalkMove

===================
*/
static void PM_WalkMove( void ) {
	int i;
	vec3_t wishvel;
	float fmove, smove;
	vec3_t wishdir;
	float wishspeed;
	float scale;
	usercmd_t cmd;
	float accelerate;
	float vel;
//	float botBonus = 1.0;

/*#ifdef CGAMEDLL
	int gametype = cg_gameType.integer;
#elif GAMEDLL
	int gametype = g_gametype.integer;
#endif*/

	if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
		// begin swimming
		PM_WaterMove();
		return;
	}

	if ( PM_CheckJump() ) {
		// jumped away
		if ( pm->waterlevel > 1 ) {
			PM_WaterMove();
		} else {
			PM_AirMove();
		}

		if ( !( pm->cmd.serverTime - pm->pmext->jumpTime < 850 ) ) {

			pm->pmext->sprintTime -= 2500;
			if ( pm->pmext->sprintTime < 0 ) {
				pm->pmext->sprintTime = 0;
			}

			pm->pmext->jumpTime = pm->cmd.serverTime;
		}

		// JPW NERVE
		pm->ps->jumpTime = pm->cmd.serverTime;  // Arnout: NOTE : TEMP DEBUG

		return;
	} else /* if( !PM_CheckProne() )*/ {
		if ( pm->waterlevel <= 1 && PM_CheckDodge() ) {
			PM_AirMove();
			return;
		}
	}

	/*if( pm->waterlevel <= 1 && PM_CheckDodge () ) {
		PM_AirMove();
		return;
	}*/

	PM_Friction();

	fmove = pm->cmd.forwardmove;
	smove = pm->cmd.rightmove;

	cmd = pm->cmd;
	scale = PM_CmdScale( &cmd );

// Ridah, moved this down, so we use the actual movement direction
	// set the movementDir so clients can rotate the legs for strafing
//	PM_SetMovementDir();

	// project moves down to flat plane
	pml.forward[2] = 0;
	pml.right[2] = 0;

	// project the forward and right directions onto the ground plane
	PM_ClipVelocity( pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
	PM_ClipVelocity( pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
	//
	VectorNormalize( pml.forward );
	VectorNormalize( pml.right );

	for ( i = 0 ; i < 3 ; i++ ) {
		wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove;
	}
	// when going up or down slopes the wish velocity should Not be zero
//	wishvel[2] = 0;

	VectorCopy( wishvel, wishdir );
	wishspeed = VectorNormalize( wishdir );
	wishspeed *= scale;

	// clamp the speed lower if prone
	if ( pm->ps->eFlags & EF_PRONE ) {
		if ( wishspeed > pm->ps->speed * pm_proneSpeedScale ) {
			wishspeed = pm->ps->speed * pm_proneSpeedScale;
		}
	} else if ( pm->ps->pm_flags & PMF_DUCKED ) { // clamp the speed lower if ducking
		/*if ( wishspeed > pm->ps->speed * pm_duckScale ) {
			wishspeed = pm->ps->speed * pm_duckScale;
		}*/
		if ( wishspeed > pm->ps->speed * pm->ps->crouchSpeedScale ) {
			wishspeed = pm->ps->speed * pm->ps->crouchSpeedScale;
		}
	}

	// clamp the speed lower if wading or walking on the bottom
	if ( pm->waterlevel ) {
		float waterScale;

		waterScale = pm->waterlevel / 3.0;
		if ( pm->watertype == CONTENTS_SLIME ) { //----(SA)	slag
			waterScale = 1.0 - ( 1.0 - pm_slagSwimScale ) * waterScale;
		} else {
			waterScale = 1.0 - ( 1.0 - pm_waterSwimScale ) * waterScale;
		}

		if ( wishspeed > pm->ps->speed * waterScale ) {
			wishspeed = pm->ps->speed * waterScale;
		}
	}

	// when a player gets hit, they temporarily lose
	// full control, which allows them to be moved a bit
	if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
		accelerate = pm_airaccelerate;
	} else {
		accelerate = pm_accelerate;
	}

	PM_Accelerate( wishdir, wishspeed, accelerate );

	//Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
	//Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));

	if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
		pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
	} else {
		// don't reset the z velocity for slopes
		//pm->ps->velocity[2] = 0;
	}

//----(SA)	added
	// show breath when standing on 'snow' surfaces
	if ( pml.groundTrace.surfaceFlags & SURF_SNOW ) {
		pm->ps->eFlags |= EF_BREATH;
	} else {
		pm->ps->eFlags &= ~EF_BREATH;
	}
//----(SA)	end

	vel = VectorLength( pm->ps->velocity );

	// slide along the ground plane
	PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP );

	// don't do anything if standing still
	if ( !pm->ps->velocity[0] && !pm->ps->velocity[1] ) {
		if ( pm->ps->eFlags & EF_PRONE ) {
			pm->pmext->proneGroundTime = pm->cmd.serverTime;
		}
		return;
	}

	// don't decrease velocity when going up or down a slope
	VectorNormalize( pm->ps->velocity );
	VectorScale( pm->ps->velocity, vel, pm->ps->velocity );

	PM_StepSlideMove( qfalse );

// Ridah, moved this down, so we use the actual movement direction
	// set the movementDir so clients can rotate the legs for strafing
	PM_SetMovementDir();
}


/*
==============
PM_DeadMove
==============
*/
static void PM_DeadMove( void ) {
	float forward;

	if ( !pml.walking ) {
		return;
	}

	// extra friction

	forward = VectorLength( pm->ps->velocity );
	forward -= 20;
	if ( forward <= 0 ) {
		VectorClear( pm->ps->velocity );
	} else {
		VectorNormalize( pm->ps->velocity );
		VectorScale( pm->ps->velocity, forward, pm->ps->velocity );
	}
}


/*
===============
PM_NoclipMove
===============
*/
static void PM_NoclipMove( void ) {
	float speed, drop, friction, control, newspeed;
	int i;
	vec3_t wishvel;
	float fmove, smove;
	vec3_t wishdir;
	float wishspeed;
	float scale;

	pm->ps->viewheight = DEFAULT_VIEWHEIGHT;

	// friction

	speed = VectorLength( pm->ps->velocity );
	if ( speed < 1 ) {
		VectorCopy( vec3_origin, pm->ps->velocity );
	} else
	{
		drop = 0;

		friction = pm_friction * 1.5; // extra friction
		control = speed < pm_stopspeed ? pm_stopspeed : speed;
		drop += control * friction * pml.frametime;

		// scale the velocity
		newspeed = speed - drop;
		if ( newspeed < 0 ) {
			newspeed = 0;
		}
		newspeed /= speed;

		VectorScale( pm->ps->velocity, newspeed, pm->ps->velocity );
	}

	// accelerate
	scale = PM_CmdScale( &pm->cmd );

	fmove = pm->cmd.forwardmove;
	smove = pm->cmd.rightmove;

	for ( i = 0 ; i < 3 ; i++ )
		wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove;
	wishvel[2] += pm->cmd.upmove;

	VectorCopy( wishvel, wishdir );
	wishspeed = VectorNormalize( wishdir );
	wishspeed *= scale;

	PM_Accelerate( wishdir, wishspeed, pm_accelerate );

	// move
	VectorMA( pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin );
}

//============================================================================

/*
================
PM_FootstepForSurface

Returns an event number apropriate for the groundsurface
================
*/
static int PM_FootstepForSurface( void ) {
#ifdef GAMEDLL
	// In just the GAME DLL, we want to store the groundtrace surface stuff,
	// so we don't have to keep tracing.
	ClientStoreSurfaceFlags( pm->ps->clientNum, pml.groundTrace.surfaceFlags );

#endif // GAMEDLL

	return BG_FootstepForSurface( pml.groundTrace.surfaceFlags );
}

/*
=================
PM_CrashLand

Check for hard landings that generate sound events
=================
*/
static void PM_CrashLand( void ) {
	float delta;
	float dist;
	float vel, acc;
	float t;
	float a, b, c, den;

	// Ridah, only play this if coming down hard
	if ( !pm->ps->legsTimer ) {
		if ( pml.previous_velocity[2] < -220 ) {
			BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_LAND, qfalse, qtrue );
		}
	}

	// calculate the exact velocity on landing
	dist = pm->ps->origin[2] - pml.previous_origin[2];
	vel = pml.previous_velocity[2];
	acc = -pm->ps->gravity;

	a = acc / 2;
	b = vel;
	c = -dist;

	den =  b * b - 4 * a * c;
	if ( den < 0 ) {
		return;
	}
	t = ( -b - sqrt( den ) ) / ( 2 * a );

	delta = vel + t * acc;
	delta = delta * delta * 0.0001;

	// never take falling damage if completely underwater
	if ( pm->waterlevel == 3 ) {
		return;
	}

	// reduce falling damage if there is standing water
	if ( pm->waterlevel == 2 ) {
		delta *= 0.25;
	}
	if ( pm->waterlevel == 1 ) {
		delta *= 0.5;
	}

	if ( delta < 1 ) {
		return;
	}

	// create a local entity event to play the sound

	// SURF_NODAMAGE is used for bounce pads where you don't ever
	// want to take damage or play a crunch sound
	if ( !( pml.groundTrace.surfaceFlags & SURF_NODAMAGE ) ) {
		if ( pm->debugLevel ) {
			Com_Printf( "delta: %5.2f\n", delta );
		}

/* JPW NERVE removed from MP, breaks too many levels and skill as no-fall-damage indicator isn't obvious
		// Rafael gameskill
		if (bg_pmove_gameskill_integer == 1)
		{
			if (delta > 7)
				delta = 8;
		}
		// done
*/

		if ( delta > 77 ) {
			PM_AddEventExt( EV_FALL_NDIE, PM_FootstepForSurface() );
		}
		//else if (delta > 67)
		//{
		//	PM_AddEvent(EV_FALL_DMG_75);
		//}
		else if ( delta > 67 ) {
			PM_AddEventExt( EV_FALL_DMG_50, PM_FootstepForSurface() );
		}
		//else if (delta > 48)
		//{
		//	PM_AddEvent(EV_FALL_DMG_30);
		//}
		else if ( delta > 58 ) {
			// this is a pain grunt, so don't play it if dead
			if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
				PM_AddEventExt( EV_FALL_DMG_25, PM_FootstepForSurface() );
			}
		} else if ( delta > 48 )     {
			// this is a pain grunt, so don't play it if dead
			if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
				PM_AddEventExt( EV_FALL_DMG_15, PM_FootstepForSurface() );
			}
		} else if ( delta > 38.75 )     {
			// this is a pain grunt, so don't play it if dead
			if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
				PM_AddEventExt( EV_FALL_DMG_10, PM_FootstepForSurface() );
			}
		} else if ( delta > 7 )   {
			PM_AddEventExt( EV_FALL_SHORT, PM_FootstepForSurface() );
		} else
		{
			PM_AddEventExt( EV_FOOTSTEP, PM_FootstepForSurface() );
		}
	}

	// rain - when falling damage happens, velocity is cleared, but
	// this needs to happen in pmove, not g_active!  (prediction will be
	// wrong, otherwise.)
	if ( delta > 38.75 ) {
		VectorClear( pm->ps->velocity );
	}

	// start footstep cycle over
	pm->ps->bobCycle = 0;
}



/*
=============
PM_CorrectAllSolid
=============
*/
static int PM_CorrectAllSolid( trace_t *trace ) {
	int i, j, k;
	vec3_t point;

	if ( pm->debugLevel ) {
		Com_Printf( "%i:allsolid\n", c_pmove );
	}

	// jitter around
	for ( i = -1; i <= 1; i++ ) {
		for ( j = -1; j <= 1; j++ ) {
			for ( k = -1; k <= 1; k++ ) {
				VectorCopy( pm->ps->origin, point );
				point[0] += (float) i;
				point[1] += (float) j;
				point[2] += (float) k;
				PM_TraceAll( trace, point, point );
				if ( !trace->allsolid ) {
					point[0] = pm->ps->origin[0];
					point[1] = pm->ps->origin[1];
					point[2] = pm->ps->origin[2] - 0.25;

					PM_TraceAll( trace, pm->ps->origin, point );
					pml.groundTrace = *trace;
					return qtrue;
				}
			}
		}
	}

	pm->ps->groundEntityNum = ENTITYNUM_NONE;
	pml.groundPlane = qfalse;
	pml.walking = qfalse;

	return qfalse;
}


/*
=============
PM_GroundTraceMissed

The ground trace didn't hit a surface, so we are in freefall
=============
*/
static void PM_GroundTraceMissed( void ) {
	trace_t trace;
	vec3_t point;

	if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) {
		// we just transitioned into freefall
		if ( pm->debugLevel ) {
			Com_Printf( "%i:lift\n", c_pmove );
		}

		// if they aren't in a jumping animation and the ground is a ways away, force into it
		// if we didn't do the trace, the player would be backflipping down staircases
		VectorCopy( pm->ps->origin, point );
		point[2] -= 64;

		PM_TraceAll( &trace, pm->ps->origin, point );
		if ( trace.fraction == 1.0 ) {
			if ( pm->cmd.forwardmove >= 0 ) {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_JUMP, qfalse, qtrue );
				pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
			} else {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_JUMPBK, qfalse, qtrue );
				pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
			}
		}
	}

	// If we've never yet touched the ground, it's because we're spawning, so don't
	// set to "in air"
	if ( pm->ps->groundEntityNum != -1 ) {
		// Signify that we're in mid-air
		pm->ps->groundEntityNum = ENTITYNUM_NONE;

	} // if (pm->ps->groundEntityNum != -1)...
	pml.groundPlane = qfalse;
	pml.walking = qfalse;
}


/*
=============
PM_GroundTrace
=============
*/
static void PM_GroundTrace( void ) {
	vec3_t point;
	trace_t trace;

	point[0] = pm->ps->origin[0];
	point[1] = pm->ps->origin[1];


	if ( pm->ps->eFlags & EF_MG42_ACTIVE || pm->ps->eFlags & EF_AAGUN_ACTIVE ) {
		point[2] = pm->ps->origin[2] - 1.f;
	} else {
		//point[2] = pm->ps->origin[2] - 0.01f;
		point[2] = pm->ps->origin[2] - 0.25f;
	}

	PM_TraceAllLegs( &trace, &pm->pmext->proneLegsOffset, pm->ps->origin, point );
	pml.groundTrace = trace;

	// do something corrective if the trace starts in a solid...
	if ( trace.allsolid && !( pm->ps->eFlags & EF_MOUNTEDTANK ) ) {
		if ( !PM_CorrectAllSolid( &trace ) ) {
			return;
		}
	}

	// if the trace didn't hit anything, we are in free fall
	if ( trace.fraction == 1.0 ) {
		PM_GroundTraceMissed();
		pml.groundPlane = qfalse;
		pml.walking = qfalse;
		return;
	}

	// check if getting thrown off the ground
	if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 && !( pm->ps->eFlags & EF_PRONE ) ) {
		if ( pm->debugLevel ) {
			Com_Printf( "%i:kickoff\n", c_pmove );
		}
		// go into jump animation
		if ( pm->cmd.forwardmove >= 0 ) {
			BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_JUMP, qfalse, qfalse );
			pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
		} else {
			BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_JUMPBK, qfalse, qfalse );
			pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
		}

		pm->ps->groundEntityNum = ENTITYNUM_NONE;
		pml.groundPlane = qfalse;
		pml.walking = qfalse;
		return;
	}

	// slopes that are too steep will not be considered onground
	if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) {
		if ( pm->debugLevel ) {
			Com_Printf( "%i:steep\n", c_pmove );
		}
		// FIXME: if they can't slide down the slope, let them
		// walk (sharp crevices)
		pm->ps->groundEntityNum = ENTITYNUM_NONE;
		pml.groundPlane = qtrue;
		pml.walking = qfalse;
		return;
	}

	pml.groundPlane = qtrue;
	pml.walking = qtrue;

	// hitting solid ground will end a waterjump
	if ( pm->ps->pm_flags & PMF_TIME_WATERJUMP ) {
		pm->ps->pm_flags &= ~( PMF_TIME_WATERJUMP | PMF_TIME_LAND );
		pm->ps->pm_time = 0;
	}

	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
		// just hit the ground
		if ( pm->debugLevel ) {
			Com_Printf( "%i:Land\n", c_pmove );
		}

		PM_CrashLand();

		// don't do landing time if we were just going down a slope
		if ( pml.previous_velocity[2] < -200 ) {
			// don't allow another jump for a little while
			pm->ps->pm_flags |= PMF_TIME_LAND;
			pm->ps->pm_time = 250;
		}
	}

	pm->ps->groundEntityNum = trace.entityNum;

	// don't reset the z velocity for slopes
//	pm->ps->velocity[2] = 0;

	PM_AddTouchEnt( trace.entityNum );
}


/*
=============
PM_SetWaterLevel	FIXME: avoid this twice?  certainly if not moving
=============
*/
static void PM_SetWaterLevel( void ) {
	vec3_t point;
	int cont;
	int sample1;
	int sample2;

	//
	// get waterlevel, accounting for ducking
	//
	pm->waterlevel = 0;
	pm->watertype = 0;

	// Ridah, modified this
	point[0] = pm->ps->origin[0];
	point[1] = pm->ps->origin[1];
	point[2] = pm->ps->origin[2] + pm->ps->mins[2] + 1;
	cont = pm->pointcontents( point, pm->ps->clientNum );

	if ( cont & MASK_WATER ) {
		sample2 = pm->ps->viewheight - pm->ps->mins[2];
		sample1 = sample2 / 2;

		pm->watertype = cont;
		pm->waterlevel = 1;
		point[2] = pm->ps->origin[2] + pm->ps->mins[2] + sample1;
		cont = pm->pointcontents( point, pm->ps->clientNum );
		if ( cont & MASK_WATER ) {
			pm->waterlevel = 2;
			point[2] = pm->ps->origin[2] + pm->ps->mins[2] + sample2;
			cont = pm->pointcontents( point, pm->ps->clientNum );
			if ( cont & MASK_WATER ) {
				pm->waterlevel = 3;
			}
		}
	}
	// done.

	// UNDERWATER
	BG_UpdateConditionValue( pm->ps->clientNum, ANIM_COND_UNDERWATER, ( pm->waterlevel > 2 ), qtrue );

}

/*
==============
PM_CheckDuck

Sets mins, maxs, and pm->ps->viewheight
==============
*/
static void PM_CheckDuck( void ) {
	trace_t trace;

	// Ridah, modified this for configurable bounding boxes
	pm->mins[0] = pm->ps->mins[0];
	pm->mins[1] = pm->ps->mins[1];

	pm->maxs[0] = pm->ps->maxs[0];
	pm->maxs[1] = pm->ps->maxs[1];

	pm->mins[2] = pm->ps->mins[2];

	if ( pm->ps->pm_type == PM_DEAD ) {
		pm->maxs[2] = pm->ps->maxs[2];          // NOTE: must set death bounding box in game code
		pm->ps->viewheight = pm->ps->deadViewHeight;
		return;
	}

	if ( ( pm->cmd.upmove < 0 && !( pm->ps->eFlags & EF_MOUNTEDTANK ) && !( pm->ps->pm_flags & PMF_LADDER ) ) || pm->ps->weapon == WP_MORTAR_SET ) { // duck
		pm->ps->pm_flags |= PMF_DUCKED;
	} else
	{   // stand up if possible
		if ( pm->ps->pm_flags & PMF_DUCKED ) {
			// try to stand up
			pm->maxs[2] = pm->ps->maxs[2];
			PM_TraceAll( &trace, pm->ps->origin, pm->ps->origin );
			if ( !trace.allsolid ) {
				pm->ps->pm_flags &= ~PMF_DUCKED;
			}
		}
	}

	if ( pm->ps->pm_flags & PMF_DUCKED ) {
		pm->maxs[2] = pm->ps->crouchMaxZ;
		pm->ps->viewheight = pm->ps->crouchViewHeight;
	} else
	{
		pm->maxs[2] = pm->ps->maxs[2];
		pm->ps->viewheight = pm->ps->standViewHeight;
	}
	// done.
}



//===================================================================


/*
===============
PM_Footsteps
===============
*/
static void PM_Footsteps( void ) {
	float bobmove;
	int old;
	qboolean footstep;
	qboolean iswalking;
	int animResult = -1;

	if ( pm->ps->eFlags & EF_DEAD ) {


		//if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
		if ( pm->ps->pm_flags & PMF_FLAILING ) {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_FLAILING, qtrue );

			if ( !pm->ps->pm_time ) {
				pm->ps->pm_flags &= ~PMF_FLAILING;  // the eagle has landed
			}
		} else if ( !pm->ps->pm_time && !( pm->ps->pm_flags & PMF_LIMBO ) ) { // DHM - Nerve :: before going to limbo, play a wounded/fallen animation
			if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
				// takeoff!
				pm->ps->pm_flags |= PMF_FLAILING;
				animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_FLAILING, qtrue );
			} else {
				animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_FALLEN, qtrue );
			}
		}

		return;
	}

	iswalking = qfalse;

	//
	// calculate speed and cycle to be used for
	// all cyclic walking effects
	//
	pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0] +  pm->ps->velocity[1] * pm->ps->velocity[1] );

	// mg42, always idle
	if ( pm->ps->persistant[PERS_HWEAPON_USE] ) {
		animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_IDLE, qtrue );
		//
		return;
	}

	// swimming
	if ( pm->waterlevel > 2 ) {

		if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_SWIMBK, qtrue );
		} else {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_SWIM, qtrue );
		}

		return;
	}

	// in the air
	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
		if ( pm->ps->pm_flags & PMF_LADDER ) {             // on ladder
			if ( pm->ps->velocity[2] >= 0 ) {
				animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_CLIMBUP, qtrue );
				//BG_PlayAnimName( pm->ps, "BOTH_CLIMB", ANIM_BP_BOTH, qfalse, qtrue, qfalse );
			} else if ( pm->ps->velocity[2] < 0 )     {
				animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_CLIMBDOWN, qtrue );
				//BG_PlayAnimName( pm->ps, "BOTH_CLIMB_DOWN", ANIM_BP_BOTH, qfalse, qtrue, qfalse );
			}
		}

		return;
	}

	// if not trying to move
	if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
		if (  pm->xyspeed < 5 ) {
			pm->ps->bobCycle = 0;   // start at beginning of cycle again
		}
		if ( pm->xyspeed > 120 ) {
			return; // continue what they were doing last frame, until we stop
		}

		if ( pm->ps->eFlags & EF_PRONE ) {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_IDLEPRONE, qtrue );
		} else if ( pm->ps->pm_flags & PMF_DUCKED ) {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_IDLECR, qtrue );
		}

		if ( animResult < 0 ) {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_IDLE, qtrue );
		}
		//
		return;
	}

	footstep = qfalse;


	if ( pm->ps->eFlags & EF_PRONE ) {
		bobmove = 0.2;  // prone characters bob slower
		if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_PRONEBK, qtrue );
		} else {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_PRONE, qtrue );
		}
		// prone characters never play footsteps
	} else if ( pm->ps->pm_flags & PMF_DUCKED ) {
		bobmove = 0.5;  // ducked characters bob much faster
		if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_WALKCRBK, qtrue );
		} else {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_WALKCR, qtrue );
		}
		// ducked characters never play footsteps
	} else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
		if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
			bobmove = 0.4;  // faster speeds bob faster
			footstep = qtrue;
			// check for strafing
			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
				if ( pm->cmd.rightmove > 0 ) {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_STRAFERIGHT, qtrue );
				} else {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_STRAFELEFT, qtrue );
				}
			}
			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
				animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_RUNBK, qtrue );
			}
		} else {
			bobmove = 0.3;
			// check for strafing
			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
				if ( pm->cmd.rightmove > 0 ) {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_STRAFERIGHT, qtrue );
				} else {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_STRAFELEFT, qtrue );
				}
			}
			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
				animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_WALKBK, qtrue );
			}
		}

	} else {

		if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
			bobmove = 0.4;  // faster speeds bob faster
			footstep = qtrue;
			// check for strafing
			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
				if ( pm->cmd.rightmove > 0 ) {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_STRAFERIGHT, qtrue );
				} else {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_STRAFELEFT, qtrue );
				}
			}
			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
				animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_RUN, qtrue );
			}
		} else {
			bobmove = 0.3;  // walking bobs slow
			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
				if ( pm->cmd.rightmove > 0 ) {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_STRAFERIGHT, qtrue );
				} else {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_STRAFELEFT, qtrue );
				}
			}
			if ( animResult < 0 ) {   // if we havent found an anim yet, play the run
				animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_WALK, qtrue );
			}
		}
	}

	// if no anim found yet, then just use the idle as default
	if ( animResult < 0 ) {
		animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_IDLE, qtrue );
	}

	// check for footstep / splash sounds
	old = pm->ps->bobCycle;
	pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;

	// if we just crossed a cycle boundary, play an apropriate footstep event
	if ( iswalking ) {
		// sounds much more natural this way
		if ( old > pm->ps->bobCycle ) {

			if ( pm->waterlevel == 0 ) {
				if ( footstep && !pm->noFootsteps ) {
					PM_AddEventExt( EV_FOOTSTEP, PM_FootstepForSurface() );
				}
			} else if ( pm->waterlevel == 1 ) {
				// splashing
				PM_AddEvent( EV_FOOTSPLASH );
			} else if ( pm->waterlevel == 2 ) {
				// wading / swimming at surface
				PM_AddEvent( EV_SWIM );
			} else if ( pm->waterlevel == 3 ) {
				// no sound when completely underwater
			}

		}
	} else if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) {

/*		if (pm->ps->sprintExertTime && pm->waterlevel <= 2)
			PM_ExertSound ();*/

		if ( pm->waterlevel == 0 ) {
			// on ground will only play sounds if running
			if ( footstep && !pm->noFootsteps ) {
				PM_AddEventExt( EV_FOOTSTEP, PM_FootstepForSurface() );
			}
		} else if ( pm->waterlevel == 1 ) {
			// splashing
			PM_AddEvent( EV_FOOTSPLASH );
		} else if ( pm->waterlevel == 2 ) {
			// wading / swimming at surface
			PM_AddEvent( EV_SWIM );
		} else if ( pm->waterlevel == 3 ) {
			// no sound when completely underwater

		}
	}
}

/*
==============
PM_WaterEvents

Generate sound events for entering and leaving water
==============
*/
static void PM_WaterEvents( void ) {        // FIXME?
	//
	// if just entered a water volume, play a sound
	//
	if ( !pml.previous_waterlevel && pm->waterlevel ) {
		PM_AddEvent( EV_WATER_TOUCH );
	}

	//
	// if just completely exited a water volume, play a sound
	//
	if ( pml.previous_waterlevel && !pm->waterlevel ) {
		PM_AddEvent( EV_WATER_LEAVE );
	}

	//
	// check for head just going under water
	//
	if ( pml.previous_waterlevel != 3 && pm->waterlevel == 3 ) {
		PM_AddEvent( EV_WATER_UNDER );
	}

	//
	// check for head just coming out of water
	//
	if ( pml.previous_waterlevel == 3 && pm->waterlevel != 3 ) {
		if ( pm->pmext->airleft < 6000 ) {
			PM_AddEventExt( EV_WATER_CLEAR, 1 );
		} else {
			PM_AddEventExt( EV_WATER_CLEAR, 0 );
		}
	}
}


/*
==============
PM_BeginWeaponReload
==============
*/
static void PM_BeginWeaponReload( int weapon ) {
	gitem_t* item;
	int reloadTime;

	// only allow reload if the weapon isn't already occupied (firing is okay)
	if ( pm->ps->weaponstate != WEAPON_READY && pm->ps->weaponstate != WEAPON_FIRING ) {
		return;
	}

	if ( ( ( weapon == WP_CARBINE ) && pm->ps->ammoclip[WP_CARBINE] != 0 ) || ( ( weapon == WP_MOBILE_MG42 || weapon == WP_MOBILE_MG42_SET ) && pm->ps->ammoclip[WP_MOBILE_MG42] != 0 ) || ( ( weapon == WP_GARAND || weapon == WP_GARAND_SCOPE ) && pm->ps->ammoclip[WP_GARAND] != 0 ) ) {
		return; // Gordon: no reloading of the carbine until clip is empty
	}

	if ( ( weapon <= WP_NONE || weapon > WP_DYNAMITE ) && !( weapon >= WP_KAR98 && weapon < WP_NUM_WEAPONS ) ) {
		return;
	}

	item = BG_FindItemForWeapon( weapon );
	if ( !item ) {
		return;
	}
	// Gordon: fixing reloading with a full clip
	if ( pm->ps->ammoclip[item->giAmmoIndex] >= GetAmmoTableData( weapon )->maxclip ) {
		return;
	}

	// no reload when you've got a chair in your hands
/*	if(pm->ps->eFlags & EF_MELEE_ACTIVE)
		return;*/

	// no reload when leaning (this includes manual and auto reloads)
	if ( pm->ps->leanf ) {
		return;
	}

	// (SA) easier check now that the animation system handles the specifics
	switch ( weapon ) {
	case WP_DYNAMITE:
	case WP_GRENADE_LAUNCHER:
	case WP_GRENADE_PINEAPPLE:
//		case WP_LANDMINE:
//		case WP_TRIPMINE:
	case WP_SMOKE_BOMB:
		break;

	default:
		// DHM - Nerve :: override current animation (so reloading after firing will work)
		if ( pm->ps->eFlags & EF_PRONE ) {
			BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_RELOADPRONE, qfalse, qtrue );
		} else {
			BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_RELOAD, qfalse, qtrue );
		}
		break;
	}

	if ( weapon != WP_MORTAR && weapon != WP_MORTAR_SET ) {
		PM_ContinueWeaponAnim( PM_ReloadAnimForWeapon( pm->ps->weapon ) );
	}


	// okay to reload while overheating without tacking the reload time onto the end of the
	// current weaponTime (the reload time is partially absorbed into the overheat time)
	reloadTime = GetAmmoTableData( weapon )->reloadTime;
	if ( pm->skill[SK_LIGHT_WEAPONS] >= 2 && BG_isLightWeaponSupportingFastReload( weapon ) ) {
		reloadTime *= .65f;
	}
	if ( pm->ps->weaponstate == WEAPON_READY ) {
		pm->ps->weaponTime += reloadTime;
	} else if ( pm->ps->weaponTime < reloadTime ) {
		pm->ps->weaponTime += ( reloadTime - pm->ps->weaponTime );
	}

	pm->ps->weaponstate = WEAPON_RELOADING;
	PM_AddEvent( EV_FILL_CLIP );    // play reload sound
}

static void PM_ReloadClip( int weapon );

/*
===============
PM_BeginWeaponChange
===============
*/
void PM_BeginWeaponChange( int oldweapon, int newweapon, qboolean reload ) {    //----(SA)	modified to play 1st person alt-mode transition animations.
	int switchtime;
	qboolean altSwitchAnim = qfalse;

	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
		return;     // don't allow weapon switch until all buttons are up
	}

	if ( newweapon <= WP_NONE || newweapon >= WP_NUM_WEAPONS ) {
		return;
	}

	if ( !( COM_BitCheck( pm->ps->weapons, newweapon ) ) ) {
		return;
	}

	if ( pm->ps->weaponstate == WEAPON_DROPPING || pm->ps->weaponstate == WEAPON_DROPPING_TORELOAD ) {
		return;
	}

	// Gordon: don't allow change during spinup
	if ( pm->ps->weaponDelay ) {
		return;
	}

	// don't allow switch if you're holding a hot potato or dynamite
	if ( pm->ps->grenadeTimeLeft > 0 ) {
		return;
	}

	pm->ps->nextWeapon = newweapon;

	switch ( newweapon ) {
	case WP_CARBINE:
	case WP_KAR98:
		if ( newweapon != weapAlts[oldweapon] ) {
			PM_AddEvent( EV_CHANGE_WEAPON );
		}
		break;
	case WP_DYNAMITE:
	case WP_GRENADE_LAUNCHER:
	case WP_GRENADE_PINEAPPLE:
	case WP_SMOKE_BOMB:
		// initialize the timer on the potato you're switching to
		pm->ps->grenadeTimeLeft = 0;
		PM_AddEvent( EV_CHANGE_WEAPON );
		break;
	case WP_MORTAR_SET:
		if ( pm->ps->eFlags & EF_PRONE ) {
			return;
		}

		if ( pm->waterlevel == 3 ) {
			return;
		}
		PM_AddEvent( EV_CHANGE_WEAPON );
		break;
	default:
		//----(SA)	only play the weapon switch sound for the player
		PM_AddEvent( reload ? EV_CHANGE_WEAPON_2 : EV_CHANGE_WEAPON );
		break;
	}

	// it's an alt mode, play different anim
	if ( newweapon == weapAlts[oldweapon] ) {
		PM_StartWeaponAnim( PM_AltSwitchFromForWeapon( oldweapon ) );
	} else {
		PM_StartWeaponAnim( PM_DropAnimForWeapon( oldweapon ) );
	}

	switchtime = 250;   // dropping/raising usually takes 1/4 sec.
	// sometimes different switch times for alt weapons
	switch ( oldweapon ) {
	case WP_CARBINE:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 0;
			if ( !pm->ps->ammoclip[newweapon] && pm->ps->ammo[newweapon] ) {
				PM_ReloadClip( newweapon );
			}
		}
		break;
	case WP_M7:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 0;
		}
		break;
	case WP_KAR98:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 0;
			if ( !pm->ps->ammoclip[newweapon] && pm->ps->ammo[newweapon] ) {
				PM_ReloadClip( newweapon );
			}
		}
		break;
	case WP_GPG40:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 0;
		}
		break;
	case WP_LUGER:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 0;
		}
		break;
	case WP_SILENCER:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 1000;
			//switchtime = 0;
			altSwitchAnim = qtrue;
		}
		break;
	case WP_COLT:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 0;
		}
		break;
	case WP_SILENCED_COLT:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 1000;
			//switchtime = 1300;
			//switchtime = 0;
			altSwitchAnim = qtrue;
		}
		break;
	case WP_FG42:
	case WP_FG42SCOPE:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 50;        // fast
		}
		break;
	case WP_MOBILE_MG42:
		if ( newweapon == weapAlts[oldweapon] ) {
			vec3_t axis[3];

			switchtime = 0;

			VectorCopy( pml.forward, axis[0] );
			VectorCopy( pml.right, axis[2] );
			CrossProduct( axis[0], axis[2], axis[1] );
			AxisToAngles( axis, pm->pmext->mountedWeaponAngles );
		}
	case WP_MOBILE_MG42_SET:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 0;
		}
		break;
	case WP_MORTAR:
		if ( newweapon == weapAlts[oldweapon] ) {
			vec3_t axis[3];

			switchtime = 0;

			VectorCopy( pml.forward, axis[0] );
			VectorCopy( pml.right, axis[2] );
			CrossProduct( axis[0], axis[2], axis[1] );
			AxisToAngles( axis, pm->pmext->mountedWeaponAngles );
		}
		break;
	case WP_MORTAR_SET:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 0;
		}
		break;
	}

	// play an animation
	if ( altSwitchAnim ) {
		if ( pm->ps->eFlags & EF_PRONE ) {
			BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_UNDO_ALT_WEAPON_MODE_PRONE, qfalse, qfalse );
		} else {
			BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_UNDO_ALT_WEAPON_MODE, qfalse, qfalse );
		}
	} else {
		BG_AnimScriptEvent( pm->ps,pm->character->animModelInfo,  ANIM_ET_DROPWEAPON, qfalse, qfalse );
	}

	if ( reload ) {
		pm->ps->weaponstate = WEAPON_DROPPING_TORELOAD;
	} else {
		pm->ps->weaponstate = WEAPON_DROPPING;
	}

	pm->ps->weaponTime += switchtime;
}


/*
===============
PM_FinishWeaponChange
===============
*/
static void PM_FinishWeaponChange( void ) {
	int oldweapon, newweapon, switchtime;
	qboolean altSwitchAnim = qfalse;
	qboolean doSwitchAnim = qtrue;

	newweapon = pm->ps->nextWeapon;
//	pm->ps->nextWeapon = newweapon;
	if ( newweapon < WP_NONE || newweapon >= WP_NUM_WEAPONS ) {
		newweapon = WP_NONE;
	}

	if ( !( COM_BitCheck( pm->ps->weapons, newweapon ) ) ) {
		newweapon = WP_NONE;
	}

	oldweapon = pm->ps->weapon;

	pm->ps->weapon = newweapon;

	if ( pm->ps->weaponstate == WEAPON_DROPPING_TORELOAD ) {
		pm->ps->weaponstate = WEAPON_RAISING_TORELOAD;
	} else {
		pm->ps->weaponstate = WEAPON_RAISING;
	}

	switch ( newweapon )
	{
		// don't really care about anim since these weapons don't show in view.
		// However, need to set the animspreadscale so they are initally at worst accuracy
	case WP_K43_SCOPE:
	case WP_GARAND_SCOPE:
	case WP_FG42SCOPE:
		pm->ps->aimSpreadScale = 255;               // initially at lowest accuracy
		pm->ps->aimSpreadScaleFloat = 255.0f;       // initially at lowest accuracy
		break;
	case WP_SILENCER:
		pm->pmext->silencedSideArm |= 1;
		break;
	case WP_LUGER:
		pm->pmext->silencedSideArm &= ~1;
		break;
	case WP_SILENCED_COLT:
		pm->pmext->silencedSideArm |= 1;
		break;
	case WP_COLT:
		pm->pmext->silencedSideArm &= ~1;
		break;
	case WP_CARBINE:
		pm->pmext->silencedSideArm &= ~2;
		break;
	case WP_M7:
		pm->pmext->silencedSideArm |= 2;
		break;
	case WP_KAR98:
		pm->pmext->silencedSideArm &= ~2;
		break;
	case WP_GPG40:
		pm->pmext->silencedSideArm |= 2;
		break;
//		case WP_MEDIC_SYRINGE:
//			pm->pmext->silencedSideArm &= ~4;
//			break;
//		case WP_MEDIC_ADRENALINE:
//			pm->pmext->silencedSideArm |= 4;
//			break;
	default:
		break;
	}

	// doesn't happen too often (player switched weapons away then back very quickly)
	if ( oldweapon == newweapon ) {
		return;
	}

	// dropping/raising usually takes 1/4 sec.
	switchtime = 250;

	// sometimes different switch times for alt weapons
	switch ( newweapon ) {
	case WP_LUGER:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 0;
			//switchtime = 50;
			//switchtime = 1050;
			altSwitchAnim = qtrue ;
		}
		break;
	case WP_SILENCER:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 1190;
			altSwitchAnim = qtrue ;
		}
		break;
	case WP_COLT:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 0;
			//switchtime = 1050;
			altSwitchAnim = qtrue ;
		}
		break;
	case WP_SILENCED_COLT:
		if ( newweapon == weapAlts[oldweapon] ) {
			//switchtime = 1300;
			switchtime = 1190;
			altSwitchAnim = qtrue ;
		}
		break;
	case WP_CARBINE:
		if ( newweapon == weapAlts[oldweapon] ) {
			//switchtime = 2000;
			if ( pm->ps->ammoclip[ BG_FindAmmoForWeapon( oldweapon ) ] ) {
				switchtime = 1347;
			} else {
				switchtime = 0;
				doSwitchAnim = qfalse;
			}
			altSwitchAnim = qtrue ;
		}
		break;
	case WP_M7:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 2350;
			altSwitchAnim = qtrue ;
		}
		break;
	case WP_KAR98:
		if ( newweapon == weapAlts[oldweapon] ) {
			//switchtime = 2000;
			if ( pm->ps->ammoclip[ BG_FindAmmoForWeapon( oldweapon ) ] ) {
				switchtime = 1347;
			} else {
				switchtime = 0;
				doSwitchAnim = qfalse;
			}
			altSwitchAnim = qtrue ;
		}
		break;
	case WP_GPG40:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 2350;
			altSwitchAnim = qtrue ;
		}
		break;
	case WP_FG42:
	case WP_FG42SCOPE:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 50;        // fast
		}
		break;
	case WP_MOBILE_MG42:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 1722;
		}
		break;
	case WP_MOBILE_MG42_SET:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 1250;
		}
		break;
	case WP_MORTAR:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 1000;
			altSwitchAnim = qtrue;
		}
		break;
	case WP_MORTAR_SET:
		if ( newweapon == weapAlts[oldweapon] ) {
			switchtime = 1667;
			altSwitchAnim = qtrue;
		}
		break;
	}

	pm->ps->weaponTime += switchtime;

	BG_UpdateConditionValue( pm->ps->clientNum, ANIM_COND_WEAPON, newweapon, qtrue );

	// play an animation
	if ( doSwitchAnim ) {
		if ( altSwitchAnim ) {
			if ( pm->ps->eFlags & EF_PRONE ) {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_DO_ALT_WEAPON_MODE_PRONE, qfalse, qfalse );
			} else {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_DO_ALT_WEAPON_MODE, qfalse, qfalse );
			}
		} else {
			if ( pm->ps->eFlags & EF_PRONE ) {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_RAISEWEAPONPRONE, qfalse, qfalse );
			} else {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_RAISEWEAPON, qfalse, qfalse );
			}
		}

		// alt weapon switch was played when switching away, just go into idle
		if ( weapAlts[oldweapon] == newweapon ) {
			PM_StartWeaponAnim( PM_AltSwitchToForWeapon( newweapon ) );
		} else {
			PM_StartWeaponAnim( PM_RaiseAnimForWeapon( newweapon ) );
		}
	}
}


/*
==============
PM_ReloadClip
==============
*/
static void PM_ReloadClip( int weapon ) {
	int ammoreserve, ammoclip, ammomove;

	ammoreserve = pm->ps->ammo[ BG_FindAmmoForWeapon( weapon )];
	ammoclip    = pm->ps->ammoclip[BG_FindClipForWeapon( weapon )];

	ammomove = GetAmmoTableData( weapon )->maxclip - ammoclip;

	if ( ammoreserve < ammomove ) {
		ammomove = ammoreserve;
	}

	if ( ammomove ) {
		pm->ps->ammo[ BG_FindAmmoForWeapon( weapon )] -= ammomove;
		pm->ps->ammoclip[BG_FindClipForWeapon( weapon )] += ammomove;
	}

	// reload akimbo stuff
	if ( BG_IsAkimboWeapon( weapon ) ) {
		PM_ReloadClip( BG_AkimboSidearm( weapon ) );
	}
}

/*
==============
PM_FinishWeaponReload
==============
*/

static void PM_FinishWeaponReload( void ) {
	PM_ReloadClip( pm->ps->weapon );          // move ammo into clip
	pm->ps->weaponstate = WEAPON_READY;     // ready to fire
	PM_StartWeaponAnim( PM_IdleAnimForWeapon( pm->ps->weapon ) );
}


/*
==============
PM_CheckforReload
==============
*/
void PM_CheckForReload( int weapon ) {
	qboolean autoreload;
	qboolean reloadRequested;
	int clipWeap, ammoWeap;

	if ( pm->noWeapClips ) { // no need to reload
		return;
	}

	// GPG40 and M7 don't reload
	if ( weapon == WP_GPG40 || weapon == WP_M7 ) {
		return;
	}

	// user is forcing a reload (manual reload)
	reloadRequested = (qboolean)( pm->cmd.wbuttons & WBUTTON_RELOAD );

	switch ( pm->ps->weaponstate ) {
	case WEAPON_RAISING:
	case WEAPON_RAISING_TORELOAD:
	case WEAPON_DROPPING:
	case WEAPON_DROPPING_TORELOAD:
	case WEAPON_READYING:
	case WEAPON_RELAXING:
	case WEAPON_RELOADING:
		return;
	default:
		break;
	}

	autoreload = pm->pmext->bAutoReload || !IS_AUTORELOAD_WEAPON( weapon );
	clipWeap = BG_FindClipForWeapon( weapon );
	ammoWeap = BG_FindAmmoForWeapon( weapon );

	switch ( weapon ) {
	case WP_FG42SCOPE:
	case WP_GARAND_SCOPE:
	case WP_K43_SCOPE:
		if ( reloadRequested && pm->ps->ammo[ammoWeap] && pm->ps->ammoclip[clipWeap] < GetAmmoTableData( weapon )->maxclip ) {
			PM_BeginWeaponChange( weapon, weapAlts[weapon], !( pm->ps->ammo[ammoWeap] ) ? qfalse : qtrue );
		}
		return;
	default:
		break;
	}

	if ( pm->ps->weaponTime <= 0 ) {
		qboolean doReload = qfalse;

		if ( reloadRequested ) {
			if ( pm->ps->ammo[ammoWeap] ) {
				if ( pm->ps->ammoclip[clipWeap] < GetAmmoTableData( weapon )->maxclip ) {
					doReload = qtrue;
				}

				// akimbo should also check other weapon status
				if ( BG_IsAkimboWeapon( weapon ) ) {
					if ( pm->ps->ammoclip[BG_FindClipForWeapon( BG_AkimboSidearm( weapon ) )] < GetAmmoTableData( BG_FindClipForWeapon( BG_AkimboSidearm( weapon ) ) )->maxclip ) {
						doReload = qtrue;
					}
				}
			}
		} else if ( autoreload ) {
			if ( !pm->ps->ammoclip[clipWeap] && pm->ps->ammo[ammoWeap] ) {
				if ( BG_IsAkimboWeapon( weapon ) ) {
					if ( !pm->ps->ammoclip[BG_FindClipForWeapon( BG_AkimboSidearm( weapon ) )] ) {
						doReload = qtrue;
					}
				} /*else if( BG_IsAkimboSideArm(weapon, pm->ps) ) {
					if( !pm->ps->ammoclip[BG_FindClipForWeapon(BG_AkimboForSideArm(weapon))] )
						doReload = qtrue;
				}*/
				else {
					doReload = qtrue;
				}
			}
		}

		if ( doReload ) {
			PM_BeginWeaponReload( weapon );
		}
	}



}

/*
==============
PM_SwitchIfEmpty
==============
*/
static void PM_SwitchIfEmpty( void ) {
	// weapon from here down will be a thrown explosive
	if ( pm->ps->weapon != WP_GRENADE_LAUNCHER &&
		 pm->ps->weapon != WP_GRENADE_PINEAPPLE &&
		 pm->ps->weapon != WP_DYNAMITE &&
		 pm->ps->weapon != WP_SMOKE_BOMB &&
		 pm->ps->weapon != WP_LANDMINE ) {
		return;
	}

	if ( pm->ps->ammoclip[ BG_FindClipForWeapon( pm->ps->weapon )] ) { // still got ammo in clip
		return;
	}

	if ( pm->ps->ammo[ BG_FindAmmoForWeapon( pm->ps->weapon )] ) { // still got ammo in reserve
		return;
	}

	// If this was the last one, remove the weapon and switch away before the player tries to fire next

	// NOTE: giving grenade ammo to a player will re-give him the weapon (if you do it through add_ammo())
	switch ( pm->ps->weapon ) {
	case WP_GRENADE_LAUNCHER:
	case WP_GRENADE_PINEAPPLE:
	case WP_DYNAMITE:
		COM_BitClear( pm->ps->weapons, pm->ps->weapon );
		break;
	default:
		break;
	}

	PM_AddEvent( EV_NOAMMO );
}


/*
==============
PM_WeaponUseAmmo
	accounts for clips being used/not used
==============
*/
void PM_WeaponUseAmmo( int wp, int amount ) {
	int takeweapon;

	if ( pm->noWeapClips ) {
		pm->ps->ammo[ BG_FindAmmoForWeapon( wp )] -= amount;
	} else {
		takeweapon = BG_FindClipForWeapon( wp );

		if ( BG_IsAkimboWeapon( wp ) ) {
			if ( !BG_AkimboFireSequence( wp, pm->ps->ammoclip[BG_FindClipForWeapon( wp )], pm->ps->ammoclip[BG_FindClipForWeapon( BG_AkimboSidearm( wp ) )] ) ) {
				takeweapon = BG_AkimboSidearm( wp );
			}
		}

		pm->ps->ammoclip[takeweapon] -= amount;
	}
}


/*
==============
PM_WeaponAmmoAvailable
	accounts for clips being used/not used
==============
*/
int PM_WeaponAmmoAvailable( int wp ) {
	int takeweapon;

	if ( pm->noWeapClips ) {
		return pm->ps->ammo[ BG_FindAmmoForWeapon( wp )];
	} else {
		//return pm->ps->ammoclip[BG_FindClipForWeapon( wp )];
		takeweapon = BG_FindClipForWeapon( wp );

		if ( BG_IsAkimboWeapon( wp ) ) {
			if ( !BG_AkimboFireSequence( wp, pm->ps->ammoclip[BG_FindClipForWeapon( wp )], pm->ps->ammoclip[BG_FindClipForWeapon( BG_AkimboSidearm( wp ) )] ) ) {
				takeweapon = BG_AkimboSidearm( wp );
			}
		}

		return pm->ps->ammoclip[takeweapon];
	}
}

/*
==============
PM_WeaponClipEmpty
	accounts for clips being used/not used
==============
*/
int PM_WeaponClipEmpty( int wp ) {
	if ( pm->noWeapClips ) {
		if ( !( pm->ps->ammo[ BG_FindAmmoForWeapon( wp )] ) ) {
			return 1;
		}
	} else {
		if ( !( pm->ps->ammoclip[BG_FindClipForWeapon( wp )] ) ) {
			return 1;
		}
	}

	return 0;
}


/*
==============
PM_CoolWeapons
==============
*/
void PM_CoolWeapons( void ) {
	int wp, maxHeat;

	for ( wp = 0; wp < WP_NUM_WEAPONS; wp++ ) {

		// if you have the weapon
		if ( COM_BitCheck( pm->ps->weapons, wp ) ) {
			// and it's hot
			if ( pm->ps->weapHeat[wp] ) {
				if ( pm->skill[SK_HEAVY_WEAPONS] >= 2 && pm->ps->stats[STAT_PLAYER_CLASS] == PC_SOLDIER ) {
					pm->ps->weapHeat[wp] -= ( (float)GetAmmoTableData( wp )->coolRate * 2.f * pml.frametime );
				} else {
					pm->ps->weapHeat[wp] -= ( (float)GetAmmoTableData( wp )->coolRate * pml.frametime );
				}

				if ( pm->ps->weapHeat[wp] < 0 ) {
					pm->ps->weapHeat[wp] = 0;
				}

			}
		}
	}

	// a weapon is currently selected, convert current heat value to 0-255 range for client transmission
	if ( pm->ps->weapon ) {
		if ( pm->ps->persistant[PERS_HWEAPON_USE] || pm->ps->eFlags & EF_MOUNTEDTANK ) {
			// rain - floor to prevent 8-bit wrap
			pm->ps->curWeapHeat = floor( ( ( (float)pm->ps->weapHeat[WP_DUMMY_MG42] / MAX_MG42_HEAT ) ) * 255.0f );
		} else {
			// rain - #172 - don't divide by 0
			maxHeat = GetAmmoTableData( pm->ps->weapon )->maxHeat;

			// rain - floor to prevent 8-bit wrap
			if ( maxHeat != 0 ) {
				pm->ps->curWeapHeat = floor( ( ( (float)pm->ps->weapHeat[pm->ps->weapon] / (float)maxHeat ) ) * 255.0f );
			} else {
				pm->ps->curWeapHeat = 0;
			}
		}

//		if(pm->ps->weapHeat[pm->ps->weapon])
//			Com_Printf("pm heat: %d, %d\n", pm->ps->weapHeat[pm->ps->weapon], pm->ps->curWeapHeat);
	}

}

/*
==============
PM_AdjustAimSpreadScale
==============
*/
//#define	AIMSPREAD_DECREASE_RATE		300.0f
#define AIMSPREAD_DECREASE_RATE     200.0f      // (SA) when I made the increase/decrease floats (so slower weapon recover could happen for scoped weaps) the average rate increased significantly
#define AIMSPREAD_INCREASE_RATE     800.0f
#define AIMSPREAD_VIEWRATE_MIN      30.0f       // degrees per second
#define AIMSPREAD_VIEWRATE_RANGE    120.0f      // degrees per second

void PM_AdjustAimSpreadScale( void ) {
//	int		increase, decrease, i;
	int i;
	float increase, decrease;       // (SA) was losing lots of precision on slower weapons (scoped)
	float viewchange, cmdTime, wpnScale;

	// all weapons are very inaccurate in zoomed mode
	if ( pm->ps->eFlags & EF_ZOOMING ) {
		pm->ps->aimSpreadScale = 255;
		pm->ps->aimSpreadScaleFloat = 255;
		return;
	}

	cmdTime = (float)( pm->cmd.serverTime - pm->oldcmd.serverTime ) / 1000.0;

	wpnScale = 0.0f;
	switch ( pm->ps->weapon ) {
	case WP_LUGER:
	case WP_SILENCER:
	case WP_AKIMBO_LUGER:
	case WP_AKIMBO_SILENCEDLUGER:
// rain - luger and akimbo are supposed to be balanced
//		wpnScale = 0.5f;
//		break;
	case WP_COLT:
	case WP_SILENCED_COLT:
	case WP_AKIMBO_COLT:
	case WP_AKIMBO_SILENCEDCOLT:
		wpnScale = 0.4f;        // doesn't fire as fast, but easier to handle than luger
		break;
	case WP_MP40:
		wpnScale = 0.6f;        // 2 handed, but not as long as mauser, so harder to keep aim
		break;
	case WP_GARAND:
		wpnScale = 0.5f;
		break;
	case WP_K43_SCOPE:
	case WP_GARAND_SCOPE:
	case WP_FG42SCOPE:
		if ( pm->skill[SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS] >= 3 ) {
			wpnScale = 5.f;
		} else {
			wpnScale = 10.f;
		}
		break;
	case WP_K43:
		wpnScale = 0.5f;
		break;
	case WP_MOBILE_MG42:
	case WP_MOBILE_MG42_SET:
		wpnScale = 0.9f;
		break;
	case WP_FG42:
		wpnScale = 0.6f;
		break;
	case WP_THOMPSON:
		wpnScale = 0.6f;
		break;
	case WP_STEN:
		wpnScale = 0.6f;
		break;
	case WP_KAR98:
	case WP_CARBINE:
		wpnScale = 0.5f;
		break;
	}

	if ( wpnScale ) {

		// JPW NERVE crouched players recover faster (mostly useful for snipers)
		if ( pm->ps->eFlags & EF_CROUCHING || pm->ps->eFlags & EF_PRONE ) {
			wpnScale *= 0.5;
		}

		decrease = ( cmdTime * AIMSPREAD_DECREASE_RATE ) / wpnScale;

		viewchange = 0;
		// take player movement into account (even if only for the scoped weapons)
		// TODO: also check for jump/crouch and adjust accordingly
		if ( BG_IsScopedWeapon( pm->ps->weapon ) ) {
			for ( i = 0; i < 2; i++ ) {
				viewchange += fabs( pm->ps->velocity[i] );
			}
		} else {
			// take player view rotation into account
			for ( i = 0; i < 2; i++ ) {
				viewchange += fabs( SHORT2ANGLE( pm->cmd.angles[i] ) - SHORT2ANGLE( pm->oldcmd.angles[i] ) );
			}
		}

		viewchange = (float)viewchange / cmdTime;   // convert into this movement for a second
		viewchange -= AIMSPREAD_VIEWRATE_MIN / wpnScale;
		if ( viewchange <= 0 ) {
			viewchange = 0;
		} else if ( viewchange > ( AIMSPREAD_VIEWRATE_RANGE / wpnScale ) ) {
			viewchange = AIMSPREAD_VIEWRATE_RANGE / wpnScale;
		}

		// now give us a scale from 0.0 to 1.0 to apply the spread increase
		viewchange = viewchange / (float)( AIMSPREAD_VIEWRATE_RANGE / wpnScale );

		increase = (int)( cmdTime * viewchange * AIMSPREAD_INCREASE_RATE );
	} else {
		increase = 0;
		decrease = AIMSPREAD_DECREASE_RATE;
	}

	// update the aimSpreadScale
	pm->ps->aimSpreadScaleFloat += ( increase - decrease );
	if ( pm->ps->aimSpreadScaleFloat < 0 ) {
		pm->ps->aimSpreadScaleFloat = 0;
	}
	if ( pm->ps->aimSpreadScaleFloat > 255 ) {
		pm->ps->aimSpreadScaleFloat = 255;
	}

	pm->ps->aimSpreadScale = (int)pm->ps->aimSpreadScaleFloat;  // update the int for the client
}

#define weaponstateFiring ( pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_FIRINGALT )

#define GRENADE_DELAY   250

/*
==============
PM_Weapon

Generates weapon events and modifes the weapon counter
==============
*/

#define VENOM_LOW_IDLE  WEAP_IDLE1
#define VENOM_HI_IDLE   WEAP_IDLE2
#define VENOM_RAISE     WEAP_ATTACK1
#define VENOM_ATTACK    WEAP_ATTACK2
#define VENOM_LOWER     WEAP_ATTACK_LASTSHOT

//#define DO_WEAPON_DBG 1

static void PM_Weapon( void ) {
	int addTime = 0;         // TTimo: init
	int ammoNeeded;
	qboolean delayedFire;       //----(SA)  true if the delay time has just expired and this is the frame to send the fire event
	int aimSpreadScaleAdd;
	int weapattackanim;
	qboolean akimboFire;
#ifdef DO_WEAPON_DBG
	static int weaponstate_last = -1;
#endif

	// don't allow attack until all buttons are up
	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
		return;
	}

	// ignore if spectator
	if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
		return;
	}

	// check for dead player
	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
		if ( !pm->ps->pm_flags & PMF_LIMBO ) {
			PM_CoolWeapons();
		}

		//pm->ps->weapon = WP_NONE;
		return;
	}

	//%	if( pm->ps->eFlags & EF_PRONE_MOVING )
	//%		return;

	// special mounted mg42 handling
	switch ( pm->ps->persistant[PERS_HWEAPON_USE] ) {
	case 1:
//			PM_CoolWeapons(); // Gordon: Arnout says this is how it's wanted ( bleugh ) no cooldown on weaps while using mg42, but need to update heat on mg42 itself
		if ( pm->ps->weapHeat[WP_DUMMY_MG42] ) {
			pm->ps->weapHeat[WP_DUMMY_MG42] -= ( 300.f * pml.frametime );

			if ( pm->ps->weapHeat[WP_DUMMY_MG42] < 0 ) {
				pm->ps->weapHeat[WP_DUMMY_MG42] = 0;
			}

			// rain - floor() to prevent 8-bit wrap
			pm->ps->curWeapHeat = floor( ( ( (float)pm->ps->weapHeat[WP_DUMMY_MG42] / MAX_MG42_HEAT ) ) * 255.0f );
		}

		if ( pm->ps->weaponTime > 0 ) {
			pm->ps->weaponTime -= pml.msec;
			if ( pm->ps->weaponTime <= 0 ) {
				if ( !( pm->cmd.buttons & BUTTON_ATTACK ) ) {
					pm->ps->weaponTime = 0;
					return;
				}
			} else {
				return;
			}
		}

		if ( pm->cmd.buttons & BUTTON_ATTACK ) {
			if ( PM_IsSinglePlayerGame() ) {
				pm->ps->weapHeat[WP_DUMMY_MG42] += MG42_RATE_OF_FIRE_SP;
			} else {
				pm->ps->weapHeat[WP_DUMMY_MG42] += MG42_RATE_OF_FIRE_MP;
			}

			PM_AddEvent( EV_FIRE_WEAPON_MG42 );

			if ( PM_IsSinglePlayerGame() ) {
				pm->ps->weaponTime += MG42_RATE_OF_FIRE_SP;
			} else {
				pm->ps->weaponTime += MG42_RATE_OF_FIRE_MP;
			}

			BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON, qfalse, qtrue );
			pm->ps->viewlocked = 2;         // this enable screen jitter when firing

			if ( pm->ps->weapHeat[WP_DUMMY_MG42] >= MAX_MG42_HEAT ) {
				pm->ps->weaponTime = MAX_MG42_HEAT;     // cap heat to max
				PM_AddEvent( EV_WEAP_OVERHEAT );
				pm->ps->weaponTime = 2000;          // force "heat recovery minimum" to 2 sec right now
			}
		}
		return;
	case 2:
		if ( pm->ps->weaponTime > 0 ) {
			pm->ps->weaponTime -= pml.msec;
			if ( pm->ps->weaponTime <= 0 ) {
				if ( !( pm->cmd.buttons & BUTTON_ATTACK ) ) {
					pm->ps->weaponTime = 0;
					return;
				}
			} else {
				return;
			}
		}

		if ( pm->cmd.buttons & BUTTON_ATTACK ) {
			PM_AddEvent( EV_FIRE_WEAPON_AAGUN );

			pm->ps->weaponTime += AAGUN_RATE_OF_FIRE;

			BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON, qfalse, qtrue );
//				pm->ps->viewlocked = 2;		// this enable screen jitter when firing
		}
		return;
	}

	if ( pm->ps->eFlags & EF_MOUNTEDTANK ) {
//		PM_CoolWeapons(); // Gordon: Arnout says this is how it's wanted ( bleugh ) no cooldown on weaps while using mg42, but need to update heat on mg42 itself
		if ( pm->ps->weapHeat[WP_DUMMY_MG42] ) {
			pm->ps->weapHeat[WP_DUMMY_MG42] -= ( 300.f * pml.frametime );

			if ( pm->ps->weapHeat[WP_DUMMY_MG42] < 0 ) {
				pm->ps->weapHeat[WP_DUMMY_MG42] = 0;
			}

			// rain - floor() to prevent 8-bit wrap
			pm->ps->curWeapHeat = floor( ( ( (float)pm->ps->weapHeat[WP_DUMMY_MG42] / MAX_MG42_HEAT ) ) * 255.0f );
		}

		if ( pm->ps->weaponTime > 0 ) {
			pm->ps->weaponTime -= pml.msec;
			if ( pm->ps->weaponTime <= 0 ) {
				if ( !( pm->cmd.buttons & BUTTON_ATTACK ) ) {
					pm->ps->weaponTime = 0;
					return;
				}
			} else {
				return;
			}
		}

		if ( pm->cmd.buttons & BUTTON_ATTACK ) {
			pm->ps->weapHeat[WP_DUMMY_MG42] += MG42_RATE_OF_FIRE_MP;

			PM_AddEvent( EV_FIRE_WEAPON_MOUNTEDMG42 );

			pm->ps->weaponTime += MG42_RATE_OF_FIRE_MP;

			BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON, qfalse, qtrue );
			//pm->ps->viewlocked = 2;		// this enable screen jitter when firing

			if ( pm->ps->weapHeat[WP_DUMMY_MG42] >= MAX_MG42_HEAT ) {
				pm->ps->weaponTime = MAX_MG42_HEAT; // cap heat to max
				PM_AddEvent( EV_WEAP_OVERHEAT );
				pm->ps->weaponTime = 2000;      // force "heat recovery minimum" to 2 sec right now
			}

		}
		return;
	}

	pm->watertype = 0;

	if ( BG_IsAkimboWeapon( pm->ps->weapon ) ) {
		akimboFire = BG_AkimboFireSequence( pm->ps->weapon, pm->ps->ammoclip[BG_FindClipForWeapon( pm->ps->weapon )], pm->ps->ammoclip[BG_FindClipForWeapon( BG_AkimboSidearm( pm->ps->weapon ) )] );
	} else {
		akimboFire = qfalse;
	}

	// TTimo
	// show_bug.cgi?id=416
#ifdef DO_WEAPON_DBG
	if ( pm->ps->weaponstate != weaponstate_last ) {
	#ifdef CGAMEDLL
		Com_Printf( " CGAMEDLL\n" );
	#else
		Com_Printf( "!CGAMEDLL\n" );
	#endif
		switch ( pm->ps->weaponstate ) {
		case WEAPON_READY:
			Com_Printf( " -- WEAPON_READY\n" );
			break;
		case WEAPON_RAISING:
			Com_Printf( " -- WEAPON_RAISING\n" );
			break;
		case WEAPON_RAISING_TORELOAD:
			Com_Printf( " -- WEAPON_RAISING_TORELOAD\n" );
			break;
		case WEAPON_DROPPING:
			Com_Printf( " -- WEAPON_DROPPING\n" );
			break;
		case WEAPON_READYING:
			Com_Printf( " -- WEAPON_READYING\n" );
			break;
		case WEAPON_RELAXING:
			Com_Printf( " -- WEAPON_RELAXING\n" );
			break;
		case WEAPON_DROPPING_TORELOAD:
			Com_Printf( " -- WEAPON_DROPPING_TORELOAD\n" );
			break;
		case WEAPON_FIRING:
			Com_Printf( " -- WEAPON_FIRING\n" );
			break;
		case WEAPON_FIRINGALT:
			Com_Printf( " -- WEAPON_FIRINGALT\n" );
			break;
		case WEAPON_RELOADING:
			Com_Printf( " -- WEAPON_RELOADING\n" );
			break;
		}
		weaponstate_last = pm->ps->weaponstate;
	}
#endif

	// weapon cool down
	PM_CoolWeapons();

	// check for weapon recoil
	// do the recoil before setting the values, that way it will be shown next frame and not this
	if ( pm->pmext->weapRecoilTime ) {
		vec3_t muzzlebounce;
		int i, deltaTime;

		deltaTime = pm->cmd.serverTime - pm->pmext->weapRecoilTime;
		VectorCopy( pm->ps->viewangles, muzzlebounce );

		if ( deltaTime > pm->pmext->weapRecoilDuration ) {
			deltaTime = pm->pmext->weapRecoilDuration;
		}

		for ( i = pm->pmext->lastRecoilDeltaTime; i < deltaTime; i += 15 ) {
			if ( pm->pmext->weapRecoilPitch > 0.f ) {
				muzzlebounce[PITCH] -= 2*pm->pmext->weapRecoilPitch*cos( 2.5*(i) / pm->pmext->weapRecoilDuration );
				muzzlebounce[PITCH] -= 0.25 * random() * ( 1.0f - ( i ) / pm->pmext->weapRecoilDuration );
			}

			if ( pm->pmext->weapRecoilYaw > 0.f ) {
				muzzlebounce[YAW] += 0.5*pm->pmext->weapRecoilYaw*cos( 1.0 - (i)*3 / pm->pmext->weapRecoilDuration );
				muzzlebounce[YAW] += 0.5 * crandom() * ( 1.0f - ( i ) / pm->pmext->weapRecoilDuration );
			}
		}

		// set the delta angle
		for ( i = 0; i < 3; i++ ) {
			int cmdAngle;

			cmdAngle = ANGLE2SHORT( muzzlebounce[i] );
			pm->ps->delta_angles[i] = cmdAngle - pm->cmd.angles[i];
		}
		VectorCopy( muzzlebounce, pm->ps->viewangles );

		if ( deltaTime == pm->pmext->weapRecoilDuration ) {
			pm->pmext->weapRecoilTime = 0;
			pm->pmext->lastRecoilDeltaTime = 0;
		} else {
			pm->pmext->lastRecoilDeltaTime = deltaTime;
		}
	}

	delayedFire = qfalse;

	if ( pm->ps->weapon == WP_GRENADE_LAUNCHER || pm->ps->weapon == WP_GRENADE_PINEAPPLE || pm->ps->weapon == WP_DYNAMITE || pm->ps->weapon == WP_SMOKE_BOMB ) {
		if ( pm->ps->grenadeTimeLeft > 0 ) {
			qboolean forcethrow = qfalse;

			if ( pm->ps->weapon == WP_DYNAMITE ) {
				pm->ps->grenadeTimeLeft += pml.msec;

				// JPW NERVE -- in multiplayer, dynamite becomes strategic, so start timer @ 30 seconds
				if ( pm->ps->grenadeTimeLeft < 5000 ) {
					pm->ps->grenadeTimeLeft = 5000;
				}

			} else {
				pm->ps->grenadeTimeLeft -= pml.msec;

				if ( pm->ps->grenadeTimeLeft <= 100 ) { // give two frames advance notice so there's time to launch and detonate
					forcethrow = qtrue;

					pm->ps->grenadeTimeLeft = 100;
				}
			}

			if ( !( pm->cmd.buttons & BUTTON_ATTACK ) || forcethrow || pm->ps->eFlags & EF_PRONE_MOVING ) {
				if ( pm->ps->weaponDelay == GetAmmoTableData( pm->ps->weapon )->fireDelayTime || forcethrow ) {
					// released fire button.  Fire!!!
					if ( pm->ps->eFlags & EF_PRONE ) {
						if ( akimboFire ) {
							BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON2PRONE, qfalse, qtrue );
						} else {
							BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPONPRONE, qfalse, qtrue );
						}
					} else {
						if ( akimboFire ) {
							BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON2, qfalse, qtrue );
						} else {
							BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON, qfalse, qtrue );
						}
					}
				}
			} else {
				return;
			}
		}
	}

	if ( pm->ps->weaponDelay > 0 ) {
		pm->ps->weaponDelay -= pml.msec;

		if ( pm->ps->weaponDelay <= 0 ) {
			pm->ps->weaponDelay = 0;
			delayedFire = qtrue;        // weapon delay has expired.  Fire this frame

			// double check the player is still holding the fire button down for these weapons
			// so you don't get a delayed "non-fire" (fire hit and released, then shot fires)
			switch ( pm->ps->weapon ) {
			default:
				break;
			}
		}
	}

	if ( pm->ps->weaponstate == WEAPON_RELAXING ) {
		pm->ps->weaponstate = WEAPON_READY;
		return;
	}

	if ( pm->ps->eFlags & EF_PRONE_MOVING && !delayedFire ) {
		return;
	}

	// make weapon function
	if ( pm->ps->weaponTime > 0 ) {
		pm->ps->weaponTime -= pml.msec;
		if ( !( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->weaponTime < 0 ) {
			pm->ps->weaponTime = 0;
		}

		// Gordon: aha, THIS is the kewl quick fire mode :)
		// JPW NERVE -- added back for multiplayer pistol balancing
		if ( pm->ps->weapon == WP_LUGER || pm->ps->weapon == WP_COLT || pm->ps->weapon == WP_SILENCER || pm->ps->weapon == WP_SILENCED_COLT ||
			 pm->ps->weapon == WP_KAR98 || pm->ps->weapon == WP_K43 || pm->ps->weapon == WP_CARBINE || pm->ps->weapon == WP_GARAND ||
			 pm->ps->weapon == WP_GARAND_SCOPE || pm->ps->weapon == WP_K43_SCOPE || BG_IsAkimboWeapon( pm->ps->weapon ) ) {
// rain - moved releasedFire into pmext instead of ps
			if ( pm->pmext->releasedFire ) {
				if ( pm->cmd.buttons & BUTTON_ATTACK ) {
					// rain - akimbo weapons only have a 200ms delay, so
					// use a shorter time for quickfire (#255)
					if ( BG_IsAkimboWeapon( pm->ps->weapon ) ) {
						if ( pm->ps->weaponTime <= 50 ) {
							pm->ps->weaponTime = 0;
						}
					} else {
						if ( pm->ps->weaponTime <= 150 ) {
							pm->ps->weaponTime = 0;
						}
					}
				}
			} else if ( !( pm->cmd.buttons & BUTTON_ATTACK ) ) {
// rain - moved releasedFire into pmext instead of ps
				pm->pmext->releasedFire = qtrue;
			}
		}
	}


	// check for weapon change
	// can't change if weapon is firing, but can change
	// again if lowering or raising

	if ( ( pm->ps->weaponTime <= 0 || ( !weaponstateFiring && pm->ps->weaponDelay <= 0 ) ) && !delayedFire ) {
		if ( pm->ps->weapon != pm->cmd.weapon ) {
			PM_BeginWeaponChange( pm->ps->weapon, pm->cmd.weapon, qfalse ); //----(SA)	modified
		}
	}

	if ( pm->ps->weaponDelay > 0 ) {
		return;
	}

	// check for clip change
	PM_CheckForReload( pm->ps->weapon );

	if ( pm->ps->weaponTime > 0 || pm->ps->weaponDelay > 0 ) {
		return;
	}

	if ( pm->ps->weaponstate == WEAPON_RELOADING ) {
		PM_FinishWeaponReload();
	}

	// change weapon if time
	if ( pm->ps->weaponstate == WEAPON_DROPPING || pm->ps->weaponstate == WEAPON_DROPPING_TORELOAD ) {
		PM_FinishWeaponChange();
		return;
	}

	if ( pm->ps->weaponstate == WEAPON_RAISING ) {
		pm->ps->weaponstate = WEAPON_READY;

//		if( pm->ps->eFlags & EF_PRONE && pm->ps->weapon == WP_MOBILE_MG42 )
//			pm->pmext->proneMG42Zoomed = qtrue;

		PM_StartWeaponAnim( PM_IdleAnimForWeapon( pm->ps->weapon ) );
		return;
	} else if ( pm->ps->weaponstate == WEAPON_RAISING_TORELOAD ) {
		pm->ps->weaponstate = WEAPON_READY;

		PM_BeginWeaponReload( pm->ps->weapon );

		return;
	}


	if ( pm->ps->weapon == WP_NONE ) { // this is possible since the player starts with nothing
		return;
	}


	// JPW NERVE -- in multiplayer, don't allow panzerfaust or dynamite to fire if charge bar isn't full
	if ( pm->ps->weapon == WP_PANZERFAUST ) {
		if ( pm->ps->eFlags & EF_PRONE ) {
			return;
		}

		if ( pm->skill[SK_HEAVY_WEAPONS] >= 1 ) {
			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < pm->soldierChargeTime * 0.66f ) {
				return;
			}
		} else if ( pm->cmd.serverTime - pm->ps->classWeaponTime < pm->soldierChargeTime ) {
			return;
		}
	}

	if ( pm->ps->weapon == WP_GPG40 || pm->ps->weapon == WP_M7 ) {
		if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->engineerChargeTime * 0.5f ) ) {
			return;
		}
	}

	if ( pm->ps->weapon == WP_MORTAR_SET ) {
		if ( pm->skill[SK_HEAVY_WEAPONS] >= 1 ) {
			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->soldierChargeTime * 0.5f * ( 1 - 0.3f ) ) ) {
				return;
			}
		} else if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->soldierChargeTime * 0.5f ) ) {
			return;
		}

		if ( !delayedFire ) {
			pm->ps->weaponstate = WEAPON_READY;
		}
	}

	if ( pm->ps->weapon == WP_SMOKE_BOMB || pm->ps->weapon == WP_SATCHEL ) {
		if ( pm->skill[SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS] >= 2 ) {
			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->covertopsChargeTime * 0.66f ) ) {
				return;
			}
		} else if ( pm->cmd.serverTime - pm->ps->classWeaponTime < pm->covertopsChargeTime ) {
			return;
		}
	}

	if ( pm->ps->weapon == WP_LANDMINE ) {
		if ( pm->skill[SK_EXPLOSIVES_AND_CONSTRUCTION] >= 2 ) {
			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->engineerChargeTime * 0.33f ) ) {
				return;
			}
		} else if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->engineerChargeTime * 0.5f ) ) {
			return;
		}
	}

	if ( pm->ps->weapon == WP_DYNAMITE ) {
		if ( pm->skill[SK_EXPLOSIVES_AND_CONSTRUCTION] >= 3 ) {
			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->engineerChargeTime * 0.66f ) ) {
				return;
			}
		} else if ( pm->cmd.serverTime - pm->ps->classWeaponTime < pm->engineerChargeTime ) {
			return;
		}
	}

	if ( pm->ps->weapon == WP_AMMO ) {
		if ( pm->skill[SK_SIGNALS] >= 1 ) {
			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->ltChargeTime * 0.15f ) ) {
				if ( pm->cmd.buttons & BUTTON_ATTACK ) {
					BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_NOPOWER, qtrue, qfalse );
				}
				return;
			}
		} else if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->ltChargeTime * 0.25f ) ) {
			// rain - #202 - ^^ properly check ltChargeTime here, not medicChargeTime
			if ( pm->cmd.buttons & BUTTON_ATTACK ) {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_NOPOWER, qtrue, qfalse );
			}
			return;
		}
	}

	if ( pm->ps->weapon == WP_MEDKIT ) {
		if ( pm->skill[SK_FIRST_AID] >= 2 ) {
			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->medicChargeTime * 0.15f ) ) {
				if ( pm->cmd.buttons & BUTTON_ATTACK ) {
					BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_NOPOWER, qtrue, qfalse );
				}
				return;
			}
		} else if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->medicChargeTime * 0.25f ) ) {
			if ( pm->cmd.buttons & BUTTON_ATTACK ) {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_NOPOWER, qtrue, qfalse );
			}
			return;
		}
	}

	if ( pm->ps->weapon == WP_SMOKE_MARKER ) {
		if ( pm->skill[SK_SIGNALS] >= 2 ) {
			if ( pm->cmd.serverTime - pm->ps->classWeaponTime < ( pm->ltChargeTime * 0.66f ) ) {
				return;
			}
		} else if ( pm->cmd.serverTime - pm->ps->classWeaponTime < pm->ltChargeTime ) {
			return;
		}
	}

	if ( pm->ps->weapon == WP_MEDIC_ADRENALINE ) {
		if ( pm->cmd.serverTime - pm->ps->classWeaponTime < pm->medicChargeTime ) {
			return;
		}
	}

/*	if( pm->ps->weapon == WP_TRIPMINE ) {
		trace_t trace;
		vec3_t start, end, forward;

		VectorCopy( pm->ps->origin, start );
		start[2] += pm->ps->viewheight;

		AngleVectors(pm->ps->viewangles, forward, NULL, NULL);

		VectorMA(start, 64, forward, end);

		pm->trace(&trace, start, NULL, NULL, end, pm->ps->clientNum, MASK_SHOT);

		if(trace.fraction == 1.f) {
			return; // didnt hit a nearby wall
		}

		if(trace.surfaceFlags & SURF_NOIMPACT) {
			return;
		}

		if(trace.entityNum != ENTITYNUM_WORLD) {
			return; // hit a player, door, etc
		}

		VectorCopy(trace.endpos, start);
		VectorMA(start, TRIPMINE_RANGE, trace.plane.normal, end);

		pm->trace(&trace, start, NULL, NULL, end, pm->ps->clientNum, MASK_SHOT);

		if(trace.fraction == 1.f) {
			return; // gap to opposite wall was too big
		}

		if(trace.surfaceFlags & SURF_NOIMPACT) {
			return;
		}

		if(trace.entityNum != ENTITYNUM_WORLD) {
			return; // hit a player, door, etc
		}
	}*/

	// check for fire
	// if not on fire button and there's not a delayed shot this frame...
	// consider also leaning, with delayed attack reset
	if ( ( !( pm->cmd.buttons & ( BUTTON_ATTACK | WBUTTON_ATTACK2 ) ) && !delayedFire ) ||
		 ( pm->ps->leanf != 0 && pm->ps->weapon != WP_GRENADE_LAUNCHER && pm->ps->weapon != WP_GRENADE_PINEAPPLE && pm->ps->weapon != WP_SMOKE_BOMB ) ) {
		pm->ps->weaponTime  = 0;
		pm->ps->weaponDelay = 0;

		if ( weaponstateFiring ) { // you were just firing, time to relax
			PM_ContinueWeaponAnim( PM_IdleAnimForWeapon( pm->ps->weapon ) );
		}

		pm->ps->weaponstate = WEAPON_READY;
		return;
	}

	// a not mounted mortar can't fire
	if ( pm->ps->weapon == WP_MORTAR ) {
		return;
	}

#ifdef SAVEGAME_SUPPORT
	if ( pm->reloading ) {
		return;
	}
#endif // SAVEGAME_SUPPORT

	// player is zooming - no fire
	// JPW NERVE in MP, LT needs to zoom to call artillery
	if ( pm->ps->eFlags & EF_ZOOMING ) {
#ifdef GAMEDLL
		if ( pm->ps->stats[STAT_PLAYER_CLASS] == PC_FIELDOPS ) {
			pm->ps->weaponTime += 500;
			PM_AddEvent( EV_FIRE_WEAPON );
		}
#endif
		return;
	}

	// player is underwater - no fire
	if ( pm->waterlevel == 3 ) {
		if ( pm->ps->weapon != WP_KNIFE &&
			 pm->ps->weapon != WP_GRENADE_LAUNCHER &&
			 pm->ps->weapon != WP_GRENADE_PINEAPPLE &&
			 pm->ps->weapon != WP_DYNAMITE &&
			 pm->ps->weapon != WP_LANDMINE &&
			 pm->ps->weapon != WP_TRIPMINE &&
			 pm->ps->weapon != WP_SMOKE_BOMB ) {
			PM_AddEvent( EV_NOFIRE_UNDERWATER );    // event for underwater 'click' for nofire
			pm->ps->weaponTime  = 500;
			pm->ps->weaponDelay = 0;                // avoid insta-fire after water exit on delayed weapon attacks
			return;
		}
	}

	// start the animation even if out of ammo
	switch ( pm->ps->weapon )
	{
	default:
		if ( !weaponstateFiring ) {
			// delay so the weapon can get up into position before firing (and showing the flash)
			pm->ps->weaponDelay = GetAmmoTableData( pm->ps->weapon )->fireDelayTime;
		} else {
			if ( pm->ps->eFlags & EF_PRONE ) {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPONPRONE, qfalse, qtrue );
			} else {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON, qfalse, qtrue );
			}
		}
		break;
		// machineguns should continue the anim, rather than start each fire
	case WP_MP40:
	case WP_THOMPSON:
	case WP_STEN:
	case WP_MEDKIT:                     // NERVE - SMF
	case WP_PLIERS:                     // NERVE - SMF
	case WP_SMOKE_MARKER:               // NERVE - SMF
	case WP_FG42:
	case WP_FG42SCOPE:
	case WP_MOBILE_MG42:
	case WP_MOBILE_MG42_SET:
	case WP_LOCKPICK:

		if ( !weaponstateFiring ) {
			pm->ps->weaponDelay = GetAmmoTableData( pm->ps->weapon )->fireDelayTime;
		} else {
			if ( pm->ps->eFlags & EF_PRONE ) {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPONPRONE, qtrue, qtrue );
			} else {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON, qtrue, qtrue );
			}
		}
		break;
	case WP_PANZERFAUST:
	case WP_LUGER:
	case WP_COLT:
	case WP_GARAND:
	case WP_K43:
	case WP_KAR98:
	case WP_CARBINE:
	case WP_GPG40:
	case WP_M7:
	case WP_SILENCER:
	case WP_SILENCED_COLT:
	case WP_GARAND_SCOPE:
	case WP_K43_SCOPE:
	case WP_AKIMBO_COLT:
	case WP_AKIMBO_SILENCEDCOLT:
	case WP_AKIMBO_LUGER:
	case WP_AKIMBO_SILENCEDLUGER:
		if ( !weaponstateFiring ) {
			// JPW NERVE -- pfaust has spinup time in MP
			if ( pm->ps->weapon == WP_PANZERFAUST ) {
				PM_AddEvent( EV_SPINUP );
			}
			// jpw

			pm->ps->weaponDelay = GetAmmoTableData( pm->ps->weapon )->fireDelayTime;
		} else {
			if ( pm->ps->eFlags & EF_PRONE ) {
				if ( akimboFire ) {
					BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON2PRONE, qfalse, qtrue );
				} else {
					BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPONPRONE, qfalse, qtrue );
				}
			} else {
				if ( akimboFire ) {
					BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON2, qfalse, qtrue );
				} else {
					BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON, qfalse, qtrue );
				}
			}
		}
		break;
	case WP_MORTAR_SET:
		if ( !weaponstateFiring ) {
			PM_AddEvent( EV_SPINUP );
			PM_StartWeaponAnim( PM_AttackAnimForWeapon( WP_MORTAR_SET ) );
			pm->ps->weaponDelay = GetAmmoTableData( pm->ps->weapon )->fireDelayTime;
		} else {
			if ( pm->ps->eFlags & EF_PRONE ) {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPONPRONE, qfalse, qtrue );
			} else {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON, qfalse, qtrue );
			}
		}
		break;

		// melee
	case WP_KNIFE:
		if ( !delayedFire ) {
			if ( pm->ps->eFlags & EF_PRONE ) {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPONPRONE, qfalse, qfalse );
			} else {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON, qfalse, qfalse );
			}
		}
		break;

		// throw
	case WP_DYNAMITE:
	case WP_GRENADE_LAUNCHER:
	case WP_GRENADE_PINEAPPLE:
	case WP_SMOKE_BOMB:
		if ( !delayedFire ) {
			if ( PM_WeaponAmmoAvailable( pm->ps->weapon ) ) {
				if ( pm->ps->weapon == WP_DYNAMITE ) {
					pm->ps->grenadeTimeLeft = 50;
				} else {
					pm->ps->grenadeTimeLeft = 4000;     // start at four seconds and count down
				}

				PM_StartWeaponAnim( PM_AttackAnimForWeapon( pm->ps->weapon ) );
			}

			pm->ps->weaponDelay = GetAmmoTableData( pm->ps->weapon )->fireDelayTime;
		}
		break;
	case WP_LANDMINE:
		if ( !delayedFire ) {
			if ( PM_WeaponAmmoAvailable( pm->ps->weapon ) ) {
				if ( pm->ps->eFlags & EF_PRONE ) {
					BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON2PRONE, qfalse, qtrue );
				} else {
					BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON, qfalse, qtrue );
				}
			}

			pm->ps->weaponDelay = GetAmmoTableData( pm->ps->weapon )->fireDelayTime;
		}
		break;
	case WP_TRIPMINE:
	case WP_SATCHEL:
		if ( !delayedFire ) {
			if ( PM_WeaponAmmoAvailable( pm->ps->weapon ) ) {
				PM_StartWeaponAnim( PM_AttackAnimForWeapon( pm->ps->weapon ) );
			}

			pm->ps->weaponDelay = GetAmmoTableData( pm->ps->weapon )->fireDelayTime;
		}
		break;
	case WP_SATCHEL_DET:
		if ( !weaponstateFiring ) {
			PM_AddEvent( EV_SPINUP );
			pm->ps->weaponDelay = GetAmmoTableData( pm->ps->weapon )->fireDelayTime;
			PM_ContinueWeaponAnim( PM_AttackAnimForWeapon( WP_SATCHEL_DET ) );
		} else {
			if ( pm->ps->eFlags & EF_PRONE ) {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPONPRONE, qfalse, qtrue );
			} else {
				BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_FIREWEAPON, qfalse, qtrue );
			}
		}

		break;
	}

	pm->ps->weaponstate = WEAPON_FIRING;

	// Gordon: reset player disguise on firing
//	if( pm->ps->weapon != WP_SMOKE_BOMB && pm->ps->weapon != WP_SATCHEL && pm->ps->weapon != WP_SATCHEL_DET ) {	// Arnout: not for these weapons
//		pm->ps->powerups[PW_OPS_DISGUISED] = 0;
//	}

	// check for out of ammo

	ammoNeeded = GetAmmoTableData( pm->ps->weapon )->uses;

	if ( pm->ps->weapon ) {
		int ammoAvailable;
		qboolean reloading, playswitchsound = qtrue;

		ammoAvailable = PM_WeaponAmmoAvailable( pm->ps->weapon );

		if ( ammoNeeded > ammoAvailable ) {
			// you have ammo for this, just not in the clip
			reloading = (qboolean)( ammoNeeded <= pm->ps->ammo[ BG_FindAmmoForWeapon( pm->ps->weapon )] );

			// if not in auto-reload mode, and reload was not explicitely requested, just play the 'out of ammo' sound
			if ( !pm->pmext->bAutoReload && IS_AUTORELOAD_WEAPON( pm->ps->weapon ) && !( pm->cmd.wbuttons & WBUTTON_RELOAD ) ) {
				reloading = qfalse;
			}

			switch ( pm->ps->weapon ) {
				// Ridah, only play if using a triggered weapon
			case WP_DYNAMITE:
			case WP_GRENADE_LAUNCHER:
			case WP_GRENADE_PINEAPPLE:
			case WP_LANDMINE:
			case WP_TRIPMINE:
			case WP_SMOKE_BOMB:
				playswitchsound = qfalse;
				break;

				// some weapons not allowed to reload.  must switch back to primary first
			case WP_FG42SCOPE:
			case WP_GARAND_SCOPE:
			case WP_K43_SCOPE:
				reloading = qfalse;
				break;
			}

			if ( playswitchsound ) {
				if ( reloading ) {
					PM_AddEvent( EV_EMPTYCLIP );
				} else {
					PM_AddEvent( EV_NOAMMO );
				}
			}

			if ( reloading ) {
				PM_ContinueWeaponAnim( PM_ReloadAnimForWeapon( pm->ps->weapon ) );
			} else {
				PM_ContinueWeaponAnim( PM_IdleAnimForWeapon( pm->ps->weapon ) );
				pm->ps->weaponTime += 500;
			}

			return;
		}
	}

	if ( pm->ps->weaponDelay > 0 ) {
		// if it hits here, the 'fire' has just been hit and the weapon dictated a delay.
		// animations have been started, weaponstate has been set, but no weapon events yet. (except possibly EV_NOAMMO)
		// checks for delayed weapons that have already been fired are return'ed above.
		return;
	}

	if ( !( pm->ps->eFlags & EF_PRONE ) && ( pml.groundTrace.surfaceFlags & SURF_SLICK ) ) {
		float fwdmove_knockback = 0.f;
		float bckmove_knockback = 0.f;

		switch ( pm->ps->weapon ) {
		case WP_MOBILE_MG42:    fwdmove_knockback = 4000.f;
			fwdmove_knockback = 400.f;
			break;
		case WP_PANZERFAUST:    fwdmove_knockback = 32000.f;
			bckmove_knockback = 1200.f;
			break;
		case WP_FLAMETHROWER:   fwdmove_knockback = 2000.f;
			bckmove_knockback = 40.f;
			break;
		}

		if ( fwdmove_knockback > 0.f ) {
			// Add some knockback on slick
			vec3_t kvel;
			float mass = 200;

			if ( DotProduct( pml.forward, pm->ps->velocity ) > 0  ) {
				VectorScale( pml.forward, -1.f * ( fwdmove_knockback / mass ), kvel );    // -1 as we get knocked backwards
			} else {
				VectorScale( pml.forward, -1.f * ( fwdmove_knockback / mass ), kvel );    // -1 as we get knocked backwards
			}

			VectorAdd( pm->ps->velocity, kvel, pm->ps->velocity );

			if ( !pm->ps->pm_time ) {
				pm->ps->pm_time = 100;
				pm->ps->pm_flags |= PMF_TIME_KNOCKBACK;
			}
		}
	}

	// take an ammo away if not infinite
	if ( PM_WeaponAmmoAvailable( pm->ps->weapon ) != -1 ) {
		// Rafael - check for being mounted on mg42
		if ( !( pm->ps->persistant[PERS_HWEAPON_USE] ) && !( pm->ps->eFlags & EF_MOUNTEDTANK ) ) {
			PM_WeaponUseAmmo( pm->ps->weapon, ammoNeeded );
		}
	}


	// fire weapon

	// add weapon heat
	if ( GetAmmoTableData( pm->ps->weapon )->maxHeat ) {
		pm->ps->weapHeat[pm->ps->weapon] += GetAmmoTableData( pm->ps->weapon )->nextShotTime;
	}

	// first person weapon animations

	// if this was the last round in the clip, play the 'lastshot' animation
	// this animation has the weapon in a "ready to reload" state
	if ( BG_IsAkimboWeapon( pm->ps->weapon ) ) {
		if ( akimboFire ) {
			weapattackanim = WEAP_ATTACK1;
		} else {
			weapattackanim = WEAP_ATTACK2;
		}
	} else {
		if ( PM_WeaponClipEmpty( pm->ps->weapon ) ) {
			weapattackanim = PM_LastAttackAnimForWeapon( pm->ps->weapon );
		} else {
			weapattackanim = PM_AttackAnimForWeapon( pm->ps->weapon );
		}
	}

	switch ( pm->ps->weapon ) {
	case WP_GRENADE_LAUNCHER:
	case WP_GRENADE_PINEAPPLE:
	case WP_DYNAMITE:
	case WP_K43:
	case WP_KAR98:
	case WP_GPG40:
	case WP_CARBINE:
	case WP_M7:
	case WP_LANDMINE:
	case WP_TRIPMINE:
	case WP_SMOKE_BOMB:
		PM_StartWeaponAnim( weapattackanim );
		break;

	case WP_MP40:
	case WP_THOMPSON:
	case WP_STEN:
	case WP_MEDKIT:
	case WP_PLIERS:
	case WP_SMOKE_MARKER:
	case WP_SATCHEL_DET:
	case WP_MOBILE_MG42:
	case WP_MOBILE_MG42_SET:
	case WP_LOCKPICK:
		PM_ContinueWeaponAnim( weapattackanim );
		break;

	case WP_MORTAR_SET:
		break;      // no animation

	default:
		// RF, testing
//			PM_ContinueWeaponAnim(weapattackanim);
		PM_StartWeaponAnim( weapattackanim );
		break;
	}

	// JPW NERVE -- in multiplayer, pfaust fires once then switches to pistol since it's useless for a while
	if ( ( pm->ps->weapon == WP_PANZERFAUST ) || ( pm->ps->weapon == WP_SMOKE_MARKER ) || ( pm->ps->weapon == WP_DYNAMITE ) || ( pm->ps->weapon == WP_SMOKE_BOMB ) || ( pm->ps->weapon == WP_LANDMINE ) || ( pm->ps->weapon == WP_SATCHEL ) ) {
		PM_AddEvent( EV_NOAMMO );
	}
	// jpw

	if ( pm->ps->weapon == WP_SATCHEL ) {
		pm->ps->ammoclip[WP_SATCHEL_DET] = 1;
		pm->ps->ammo[WP_SATCHEL] = 0;
		pm->ps->ammoclip[WP_SATCHEL] = 0;
		PM_BeginWeaponChange( WP_SATCHEL, WP_SATCHEL_DET, qfalse );
	}

	// WP_M7 and WP_GPG40 run out of ammo immediately after firing their last grenade
	if ( ( pm->ps->weapon == WP_M7 || pm->ps->weapon == WP_GPG40 ) && !pm->ps->ammo[ BG_FindAmmoForWeapon( pm->ps->weapon )] ) {
		PM_AddEvent( EV_NOAMMO );
	}

	if ( pm->ps->weapon == WP_MORTAR_SET && !pm->ps->ammo[WP_MORTAR] ) {
		PM_AddEvent( EV_NOAMMO );
		//PM_BeginWeaponChange( WP_MORTAR_SET, WP_MORTAR, qfalse );
	}

	if ( BG_IsAkimboWeapon( pm->ps->weapon ) ) {
		if ( akimboFire ) {
			PM_AddEvent( EV_FIRE_WEAPON );
		} else {
			PM_AddEvent( EV_FIRE_WEAPONB );
		}
	} else {
		if ( PM_WeaponClipEmpty( pm->ps->weapon ) ) {
			PM_AddEvent( EV_FIRE_WEAPON_LASTSHOT );
		} else {
			PM_AddEvent( EV_FIRE_WEAPON );
		}
	}

	// RF
// rain - moved releasedFire into pmext instead of ps
	pm->pmext->releasedFire = qfalse;
	pm->ps->lastFireTime = pm->cmd.serverTime;

	aimSpreadScaleAdd = 0;

	switch ( pm->ps->weapon ) {
	case WP_KNIFE:
	case WP_PANZERFAUST:
	case WP_DYNAMITE:
	case WP_GRENADE_LAUNCHER:
	case WP_GRENADE_PINEAPPLE:
	case WP_FLAMETHROWER:
	case WP_GPG40:
	case WP_M7:
	case WP_LANDMINE:
	case WP_TRIPMINE:
	case WP_SMOKE_BOMB:
	case WP_MORTAR_SET:
		addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;
		break;

	case WP_LUGER:
	case WP_SILENCER:
		addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;
// rain - colt and luger are supposed to be balanced
//		aimSpreadScaleAdd = 35;
		aimSpreadScaleAdd = 20;
		break;

	case WP_COLT:
	case WP_SILENCED_COLT:
		addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;
		aimSpreadScaleAdd = 20;
		break;

	case WP_AKIMBO_COLT:
	case WP_AKIMBO_SILENCEDCOLT:
		// if you're firing an akimbo weapon, and your other gun is dry,
		// nextshot needs to take 2x time
		addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;

		// added check for last shot in both guns so there's no delay for the last shot
		if ( !pm->ps->ammoclip[BG_FindClipForWeapon( pm->ps->weapon )] ) {
			if ( !akimboFire ) {
				addTime = 2 * GetAmmoTableData( pm->ps->weapon )->nextShotTime;
			}
		} else if ( !pm->ps->ammoclip[BG_FindClipForWeapon( BG_AkimboSidearm( pm->ps->weapon ) )] ) {
			if ( akimboFire ) {
				addTime = 2 * GetAmmoTableData( pm->ps->weapon )->nextShotTime;
			}
		}

		aimSpreadScaleAdd = 20;
		break;

	case WP_AKIMBO_LUGER:
	case WP_AKIMBO_SILENCEDLUGER:
		// if you're firing an akimbo weapon, and your other gun is dry,
		// nextshot needs to take 2x time
		addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;

		// rain - fixed the swapped usage of akimboFire vs. the colt
		// so that the last shot isn't delayed
		if ( !pm->ps->ammoclip[BG_FindClipForWeapon( pm->ps->weapon )] ) {
			if ( !akimboFire ) {
				addTime = 2 * GetAmmoTableData( pm->ps->weapon )->nextShotTime;
			}
		} else if ( !pm->ps->ammoclip[BG_FindClipForWeapon( BG_AkimboSidearm( pm->ps->weapon ) )] ) {
			if ( akimboFire ) {
				addTime = 2 * GetAmmoTableData( pm->ps->weapon )->nextShotTime;
			}
		}

// rain - colt and luger are supposed to be balanced
//		aimSpreadScaleAdd = 35;
		aimSpreadScaleAdd = 20;
		break;

	case WP_GARAND:
	case WP_K43:
	case WP_KAR98:
	case WP_CARBINE:
		addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;
		aimSpreadScaleAdd = 50;
		break;

	case WP_GARAND_SCOPE:
	case WP_K43_SCOPE:
		addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;

		aimSpreadScaleAdd = 200;
		// jpw

		break;

	case WP_FG42:
	case WP_FG42SCOPE:
		addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;
		aimSpreadScaleAdd = 200 / 2.f;
		break;

	case WP_MP40:
	case WP_THOMPSON:
		addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;
		aimSpreadScaleAdd = 15 + rand() % 10;   // (SA) new values for DM
		break;

	case WP_STEN:
		addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;
		aimSpreadScaleAdd = 15 + rand() % 10;   // (SA) new values for DM
		break;

	case WP_MOBILE_MG42:
	case WP_MOBILE_MG42_SET:
		if ( weapattackanim == WEAP_ATTACK_LASTSHOT ) {
			addTime = 2000;
		} else {
			addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;
		}
		aimSpreadScaleAdd = 20;
		break;
// JPW NERVE
	case WP_MEDIC_SYRINGE:
	case WP_MEDIC_ADRENALINE:
	case WP_AMMO:
		// TAT 1/30/2003 - lockpick will use value in table too
	case WP_LOCKPICK:
		addTime = GetAmmoTableData( pm->ps->weapon )->nextShotTime;
		break;
// jpw
		// JPW: engineers disarm bomb "on the fly" (high sample rate) but medics & LTs throw out health pack/smoke grenades slow
		// NERVE - SMF
	case WP_PLIERS:
		addTime = 50;
		break;
	case WP_MEDKIT:
		addTime = 1000;
		break;
	case WP_SMOKE_MARKER:
		addTime = 1000;
		break;
		// -NERVE - SMF
	default:
		break;
	}

	// set weapon recoil
	pm->pmext->lastRecoilDeltaTime = 0;

	switch ( pm->ps->weapon ) {
	case WP_GARAND_SCOPE:
	case WP_K43_SCOPE:
		pm->pmext->weapRecoilTime = pm->cmd.serverTime;
		pm->pmext->weapRecoilDuration = 300;
		pm->pmext->weapRecoilYaw = crandom() * .5f;

		if ( pm->skill[SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS] >= 3 ) {
			pm->pmext->weapRecoilPitch = .25f;
		} else {
			pm->pmext->weapRecoilPitch = .5f;
		}
		break;
	case WP_MOBILE_MG42:
		pm->pmext->weapRecoilTime = pm->cmd.serverTime;
		pm->pmext->weapRecoilDuration = 200;
		if ( pm->ps->pm_flags & PMF_DUCKED || pm->ps->eFlags & EF_PRONE ) {
			pm->pmext->weapRecoilYaw = crandom() * .5f;
			pm->pmext->weapRecoilPitch = .45f * random() * .15f;
		} else {
			pm->pmext->weapRecoilYaw = crandom() * .25f;
			pm->pmext->weapRecoilPitch = .75f * random() * .2f;
		}
		break;
		/*case WP_MOBILE_MG42_SET:
			pm->pmext->weapRecoilTime = 0;
			pm->pmext->weapRecoilYaw = 0.f;
			break;*/
	case WP_FG42SCOPE:
		pm->pmext->weapRecoilTime = pm->cmd.serverTime;
		pm->pmext->weapRecoilDuration = 100;
		pm->pmext->weapRecoilYaw = 0.f;
		pm->pmext->weapRecoilPitch = .45f * random() * .15f;

		if ( pm->skill[SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS] >= 3 ) {
			pm->pmext->weapRecoilPitch *= .5f;
		}
		break;
	case WP_LUGER:
	case WP_SILENCER:
	case WP_AKIMBO_LUGER:
	case WP_AKIMBO_SILENCEDLUGER:
	case WP_COLT:
	case WP_SILENCED_COLT:
	case WP_AKIMBO_COLT:
	case WP_AKIMBO_SILENCEDCOLT:
		pm->pmext->weapRecoilTime = pm->cmd.serverTime;
		pm->pmext->weapRecoilDuration = pm->skill[SK_LIGHT_WEAPONS] >= 3 ? 70 : 100;
		pm->pmext->weapRecoilYaw = 0.f; //crandom() * .1f;
		pm->pmext->weapRecoilPitch = pm->skill[SK_LIGHT_WEAPONS] >= 3 ? .25f * random() * .15f : .45f * random() * .15f;
		break;
	default:
		pm->pmext->weapRecoilTime = 0;
		pm->pmext->weapRecoilYaw = 0.f;
		break;
	}

	// check for overheat

	// the weapon can overheat, and it's hot
	if ( GetAmmoTableData( pm->ps->weapon )->maxHeat && pm->ps->weapHeat[pm->ps->weapon] ) {
		// it is overheating
		if ( pm->ps->weapHeat[pm->ps->weapon] >= GetAmmoTableData( pm->ps->weapon )->maxHeat ) {
			pm->ps->weapHeat[pm->ps->weapon] = GetAmmoTableData( pm->ps->weapon )->maxHeat;   // cap heat to max
			PM_AddEvent( EV_WEAP_OVERHEAT );
//			PM_StartWeaponAnim(WEAP_IDLE1);	// removed.  client handles anim in overheat event
			addTime = 2000;     // force "heat recovery minimum" to 2 sec right now
		}
	}

	// add the recoil amount to the aimSpreadScale
//	pm->ps->aimSpreadScale += 3.0*aimSpreadScaleAdd;
//	if (pm->ps->aimSpreadScale > 255) pm->ps->aimSpreadScale = 255;
	pm->ps->aimSpreadScaleFloat += 3.0 * aimSpreadScaleAdd;
	if ( pm->ps->aimSpreadScaleFloat > 255 ) {
		pm->ps->aimSpreadScaleFloat = 255;
	}

	if ( pm->skill[SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS] >= 3 && pm->ps->stats[STAT_PLAYER_CLASS] == PC_COVERTOPS ) {
		pm->ps->aimSpreadScaleFloat *= .5f;
	}

	pm->ps->aimSpreadScale = (int)( pm->ps->aimSpreadScaleFloat );

	pm->ps->weaponTime += addTime;

	PM_SwitchIfEmpty();
}


/*
================
PM_Animate
================
*/
#define MYTIMER_SALUTE   1133   // 17 frames, 15 fps
#define MYTIMER_DISMOUNT 667    // 10 frames, 15 fps

/*
================
PM_DropTimers
================
*/
static void PM_DropTimers( void ) {
	// drop misc timing counter
	if ( pm->ps->pm_time ) {
		if ( pml.msec >= pm->ps->pm_time ) {
			pm->ps->pm_flags &= ~PMF_ALL_TIMES;
			pm->ps->pm_time = 0;
		} else {
			pm->ps->pm_time -= pml.msec;
		}
	}

	// drop animation counter
	if ( pm->ps->legsTimer > 0 ) {
		pm->ps->legsTimer -= pml.msec;
		if ( pm->ps->legsTimer < 0 ) {
			pm->ps->legsTimer = 0;
		}
	}

	if ( pm->ps->torsoTimer > 0 ) {
		pm->ps->torsoTimer -= pml.msec;
		if ( pm->ps->torsoTimer < 0 ) {
			pm->ps->torsoTimer = 0;
		}
	}

	// first person weapon counter
	if ( pm->pmext->weapAnimTimer > 0 ) {
		pm->pmext->weapAnimTimer -= pml.msec;
		if ( pm->pmext->weapAnimTimer < 0 ) {
			pm->pmext->weapAnimTimer = 0;
		}
	}
}



#define LEAN_MAX    28.0f
#define LEAN_TIME_TO    200.0f  // time to get to/from full lean
#define LEAN_TIME_FR    300.0f  // time to get to/from full lean

/*
==============
PM_CalcLean

==============
*/
void PM_UpdateLean( playerState_t *ps, usercmd_t *cmd, pmove_t *tpm ) {
	vec3_t start, end, tmins, tmaxs, right;
	int leaning = 0;            // -1 left, 1 right
	float leanofs = 0;
	vec3_t viewangles;
	trace_t trace;

	if ( ( cmd->wbuttons & ( WBUTTON_LEANLEFT | WBUTTON_LEANRIGHT ) )  && !cmd->forwardmove && cmd->upmove <= 0 ) {
		// if both are pressed, result is no lean
		if ( cmd->wbuttons & WBUTTON_LEANLEFT ) {
			leaning -= 1;
		}
		if ( cmd->wbuttons & WBUTTON_LEANRIGHT ) {
			leaning += 1;
		}
	}

	if ( BG_PlayerMounted( ps->eFlags ) ) {
		leaning = 0;    // leaning not allowed on mg42
	}

	if ( ps->eFlags & EF_FIRING ) {
		leaning = 0;    // not allowed to lean while firing

	}
	// ATVI Wolfenstein Misc #479 - initial fix to #270 would crash in g_synchronousClients 1 situation
	if ( ps->weaponstate == WEAPON_FIRING && ps->weapon == WP_DYNAMITE ) {
		leaning = 0; // not allowed while tossing dynamite

	}
	if ( ps->eFlags & EF_PRONE || ps->weapon == WP_MORTAR_SET ) {
		leaning = 0;    // not allowed to lean while prone

	}
	leanofs = ps->leanf;


	if ( !leaning ) {  // go back to center position
		if ( leanofs > 0 ) {        // right
			//FIXME: play lean anim backwards?
			leanofs -= ( ( (float)pml.msec / (float)LEAN_TIME_FR ) * LEAN_MAX );
			if ( leanofs < 0 ) {
				leanofs = 0;
			}
		} else if ( leanofs < 0 )   { // left
			//FIXME: play lean anim backwards?
			leanofs += ( ( (float)pml.msec / (float)LEAN_TIME_FR ) * LEAN_MAX );
			if ( leanofs > 0 ) {
				leanofs = 0;
			}
		}
	}

	if ( leaning ) {
		if ( leaning > 0 ) {   // right
			if ( leanofs < LEAN_MAX ) {
				leanofs += ( ( (float)pml.msec / (float)LEAN_TIME_TO ) * LEAN_MAX );
			}

			if ( leanofs > LEAN_MAX ) {
				leanofs = LEAN_MAX;
			}

		} else {              // left
			if ( leanofs > -LEAN_MAX ) {
				leanofs -= ( ( (float)pml.msec / (float)LEAN_TIME_TO ) * LEAN_MAX );
			}

			if ( leanofs < -LEAN_MAX ) {
				leanofs = -LEAN_MAX;
			}

		}
	}

	ps->leanf = leanofs;

	if ( leaning ) {
		VectorCopy( ps->origin, start );
		start[2] += ps->viewheight;

		VectorCopy( ps->viewangles, viewangles );
		viewangles[ROLL] += leanofs / 2.0f;
		AngleVectors( viewangles, NULL, right, NULL );
		VectorMA( start, leanofs, right, end );

		VectorSet( tmins, -8, -8, -7 ); // ATVI Wolfenstein Misc #472, bumped from -4 to cover gun clipping issue
		VectorSet( tmaxs, 8, 8, 4 );

		if ( pm ) {
			pm->trace( &trace, start, tmins, tmaxs, end, ps->clientNum, MASK_PLAYERSOLID );
		} else {
			tpm->trace( &trace, start, tmins, tmaxs, end, ps->clientNum, MASK_PLAYERSOLID );
		}

		ps->leanf *= trace.fraction;
	}


	if ( ps->leanf ) {
		cmd->rightmove = 0;     // also disallowed in cl_input ~391

	}
}



/*
================
PM_UpdateViewAngles

This can be used as another entry point when only the viewangles
are being updated isntead of a full move

	!! NOTE !! Any changes to mounted/prone view should be duplicated in BotEntityWithinView()
================
*/
// rain - take a tracemask as well - we can't use anything out of pm
void PM_UpdateViewAngles( playerState_t *ps, pmoveExt_t *pmext, usercmd_t *cmd, void( trace ) ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask ), int tracemask ) { //----(SA)	modified
	short temp;
	int i;
	pmove_t tpm;
	vec3_t oldViewAngles;

	// DHM - Nerve :: Added support for PMF_TIME_LOCKPLAYER
	if ( ps->pm_type == PM_INTERMISSION || ps->pm_flags & PMF_TIME_LOCKPLAYER ) {
		return;     // no view changes at all
	}

	if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {

		// DHM - Nerve :: Allow players to look around while 'wounded' or lock to a medic if nearby
		temp = cmd->angles[1] + ps->delta_angles[1];
		// rain - always allow this.  viewlocking will take precedence
		// if a medic is found
		// rain - using a full short and converting on the client so that
		// we get >1 degree resolution
		ps->stats[STAT_DEAD_YAW] = temp;
		return;     // no view changes at all
	}

	VectorCopy( ps->viewangles, oldViewAngles );

	// circularly clamp the angles with deltas
	for ( i = 0 ; i < 3 ; i++ ) {
		temp = cmd->angles[i] + ps->delta_angles[i];
		if ( i == PITCH ) {
			// don't let the player look up or down more than 90 degrees
			if ( temp > 16000 ) {
				ps->delta_angles[i] = 16000 - cmd->angles[i];
				temp = 16000;
			} else if ( temp < -16000 ) {
				ps->delta_angles[i] = -16000 - cmd->angles[i];
				temp = -16000;
			}
		}
		ps->viewangles[i] = SHORT2ANGLE( temp );
	}

	if ( BG_PlayerMounted( ps->eFlags ) ) {
		float yaw, oldYaw;
		float degsSec = MG42_YAWSPEED;
		float arcMin, arcMax, arcDiff;

		yaw = ps->viewangles[YAW];
		oldYaw = oldViewAngles[YAW];

		if ( yaw - oldYaw > 180 ) {
			yaw -= 360;
		}
		if ( yaw - oldYaw < -180 ) {
			yaw += 360;
		}

		if ( yaw > oldYaw ) {
			if ( yaw - oldYaw > degsSec * pml.frametime ) {
				ps->viewangles[YAW] = oldYaw + degsSec * pml.frametime;

				// Set delta_angles properly
				ps->delta_angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - cmd->angles[YAW];
			}
		} else if ( oldYaw > yaw ) {
			if ( oldYaw - yaw > degsSec * pml.frametime ) {
				ps->viewangles[YAW] = oldYaw - degsSec * pml.frametime;

				// Set delta_angles properly
				ps->delta_angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - cmd->angles[YAW];
			}
		}

		// limit harc and varc

		// pitch (varc)
		arcMax = pmext->varc;
		if ( ps->eFlags & EF_AAGUN_ACTIVE ) {
			arcMin = 0;
		} else if ( ps->eFlags & EF_MOUNTEDTANK ) {
			float angle;

			arcMin = 14;
			arcMax = 50;

			angle = cos( DEG2RAD( AngleNormalize180( pmext->centerangles[ 1 ] - ps->viewangles[ 1 ] ) ) );
			angle = -AngleNormalize360( angle * AngleNormalize180( 0 - pmext->centerangles[ 0 ] ) );

			pmext->centerangles[ PITCH ] = angle;
		} else {
			arcMin = pmext->varc / 2;
		}

		arcDiff = AngleNormalize180( ps->viewangles[PITCH] - pmext->centerangles[PITCH] );

		if ( arcDiff > arcMin ) {
			ps->viewangles[PITCH] = AngleNormalize180( pmext->centerangles[PITCH] + arcMin );

			// Set delta_angles properly
			ps->delta_angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] ) - cmd->angles[PITCH];
		} else if ( arcDiff < -arcMax ) {
			ps->viewangles[PITCH] = AngleNormalize180( pmext->centerangles[PITCH] - arcMax );

			// Set delta_angles properly
			ps->delta_angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] ) - cmd->angles[PITCH];
		}

		if ( !( ps->eFlags & EF_MOUNTEDTANK ) ) {
			// yaw (harc)
			arcMin = arcMax = pmext->harc;
			arcDiff = AngleNormalize180( ps->viewangles[YAW] - pmext->centerangles[YAW] );

			if ( arcDiff > arcMin ) {
				ps->viewangles[YAW] = AngleNormalize180( pmext->centerangles[YAW] + arcMin );

				// Set delta_angles properly
				ps->delta_angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - cmd->angles[YAW];
			} else if ( arcDiff < -arcMax ) {
				ps->viewangles[YAW] = AngleNormalize180( pmext->centerangles[YAW] - arcMax );

				// Set delta_angles properly
				ps->delta_angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - cmd->angles[YAW];
			}
		}
	} else if ( ps->weapon == WP_MORTAR_SET ) {
		float degsSec = 60.f;
		float yaw, oldYaw;
		float pitch, oldPitch;
		float pitchMax = 30.f;
		float yawDiff, pitchDiff;

		yaw = ps->viewangles[YAW];
		oldYaw = oldViewAngles[YAW];

		if ( yaw - oldYaw > 180 ) {
			yaw -= 360;
		}
		if ( yaw - oldYaw < -180 ) {
			yaw += 360;
		}

		if ( yaw > oldYaw ) {
			if ( yaw - oldYaw > degsSec * pml.frametime ) {
				ps->viewangles[YAW] = oldYaw + degsSec * pml.frametime;

				// Set delta_angles properly
				ps->delta_angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - cmd->angles[YAW];
			}
		} else if ( oldYaw > yaw ) {
			if ( oldYaw - yaw > degsSec * pml.frametime ) {
				ps->viewangles[YAW] = oldYaw - degsSec * pml.frametime;

				// Set delta_angles properly
				ps->delta_angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - cmd->angles[YAW];
			}
		}

		pitch = ps->viewangles[PITCH];
		oldPitch = oldViewAngles[PITCH];

		if ( pitch - oldPitch > 180 ) {
			pitch -= 360;
		}
		if ( pitch - oldPitch < -180 ) {
			pitch += 360;
		}

		if ( pitch > oldPitch ) {
			if ( pitch - oldPitch > degsSec * pml.frametime ) {
				ps->viewangles[PITCH] = oldPitch + degsSec * pml.frametime;

				// Set delta_angles properly
				ps->delta_angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] ) - cmd->angles[PITCH];
			}
		} else if ( oldPitch > pitch ) {
			if ( oldPitch - pitch > degsSec * pml.frametime ) {
				ps->viewangles[PITCH] = oldPitch - degsSec * pml.frametime;

				// Set delta_angles properly
				ps->delta_angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] ) - cmd->angles[PITCH];
			}
		}

		// yaw
		yawDiff = ps->viewangles[YAW] - pmext->mountedWeaponAngles[YAW];

		if ( yawDiff > 180 ) {
			yawDiff -= 360;
		} else if ( yawDiff < -180 ) {
			yawDiff += 360;
		}

		if ( yawDiff > 30 ) {
			ps->viewangles[YAW] = AngleNormalize180( pmext->mountedWeaponAngles[YAW] + 30.f );

			// Set delta_angles properly
			ps->delta_angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - cmd->angles[YAW];
		} else if ( yawDiff < -30 ) {
			ps->viewangles[YAW] = AngleNormalize180( pmext->mountedWeaponAngles[YAW] - 30.f );

			// Set delta_angles properly
			ps->delta_angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - cmd->angles[YAW];
		}

		// pitch
		pitchDiff = ps->viewangles[PITCH] - pmext->mountedWeaponAngles[PITCH];

		if ( pitchDiff > 180 ) {
			pitchDiff -= 360;
		} else if ( pitchDiff < -180 ) {
			pitchDiff += 360;
		}

		if ( pitchDiff > ( pitchMax - 10.f ) ) {
			ps->viewangles[PITCH] = AngleNormalize180( pmext->mountedWeaponAngles[PITCH] + ( pitchMax - 10.f ) );

			// Set delta_angles properly
			ps->delta_angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] ) - cmd->angles[PITCH];
		} else if ( pitchDiff < -( pitchMax ) ) {
			ps->viewangles[PITCH] = AngleNormalize180( pmext->mountedWeaponAngles[PITCH] - ( pitchMax ) );

			// Set delta_angles properly
			ps->delta_angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] ) - cmd->angles[PITCH];
		}
	} else if ( ps->eFlags & EF_PRONE ) {
		//float degsSec = 60.f;
		float /*yaw, */ oldYaw;
		trace_t traceres; // rain - renamed
		int newDeltaAngle = ps->delta_angles[YAW];
		float pitchMax = 40.f;
		float yawDiff, pitchDiff;

		//yaw = ps->viewangles[YAW];
		oldYaw = oldViewAngles[YAW];

		/*if ( yaw - oldYaw > 180 ) {
			yaw -= 360;
		}
		if ( yaw - oldYaw < -180 ) {
			yaw += 360;
		}

		if( yaw > oldYaw ) {
			if( yaw - oldYaw > degsSec * pml.frametime ) {
				ps->viewangles[YAW] = oldYaw + degsSec * pml.frametime;

				// Set delta_angles properly
				newDeltaAngle = ANGLE2SHORT(ps->viewangles[YAW]) - cmd->angles[YAW];
			}
		} else if( oldYaw > yaw ) {
			if( oldYaw - yaw > degsSec * pml.frametime ) {
				ps->viewangles[YAW] = oldYaw - degsSec * pml.frametime;

				// Set delta_angles properly
				newDeltaAngle = ANGLE2SHORT(ps->viewangles[YAW]) - cmd->angles[YAW];
			}
		}*/

		// Check if we are allowed to rotate to there
		if ( ps->weapon == WP_MOBILE_MG42_SET ) {
			pitchMax = 20.f;

			// yaw
			yawDiff = ps->viewangles[YAW] - pmext->mountedWeaponAngles[YAW];

			if ( yawDiff > 180 ) {
				yawDiff -= 360;
			} else if ( yawDiff < -180 ) {
				yawDiff += 360;
			}

			if ( yawDiff > 20 ) {
				ps->viewangles[YAW] = AngleNormalize180( pmext->mountedWeaponAngles[YAW] + 20.f );

				// Set delta_angles properly
				ps->delta_angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - cmd->angles[YAW];
			} else if ( yawDiff < -20 ) {
				ps->viewangles[YAW] = AngleNormalize180( pmext->mountedWeaponAngles[YAW] - 20.f );

				// Set delta_angles properly
				ps->delta_angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - cmd->angles[YAW];
			}
		}

		// pitch
		pitchDiff = ps->viewangles[PITCH] - pmext->mountedWeaponAngles[PITCH];

		if ( pitchDiff > 180 ) {
			pitchDiff -= 360;
		} else if ( pitchDiff < -180 ) {
			pitchDiff += 360;
		}

		if ( pitchDiff > pitchMax ) {
			ps->viewangles[PITCH] = AngleNormalize180( pmext->mountedWeaponAngles[PITCH] + pitchMax );

			// Set delta_angles properly
			ps->delta_angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] ) - cmd->angles[PITCH];
		} else if ( pitchDiff < -pitchMax ) {
			ps->viewangles[PITCH] = AngleNormalize180( pmext->mountedWeaponAngles[PITCH] - pitchMax );

			// Set delta_angles properly
			ps->delta_angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] ) - cmd->angles[PITCH];
		}

		// Check if we rotated into a wall with our legs, if so, undo yaw
		if ( ps->viewangles[YAW] != oldYaw ) {
			// see if we have the space to go prone
			// we know our main body isn't in a solid, check for our legs

			// rain - bugfix - use supplied trace - pm may not be set
			PM_TraceLegs( &traceres, &pmext->proneLegsOffset, ps->origin, ps->origin, NULL, ps->viewangles, pm->trace, ps->clientNum, tracemask );

			if ( traceres.allsolid /* && trace.entityNum >= MAX_CLIENTS */ ) {
				// starting in a solid, no space
				ps->viewangles[YAW] = oldYaw;
				ps->delta_angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - cmd->angles[YAW];
			} else {
				// all fine
				ps->delta_angles[YAW] = newDeltaAngle;
			}
		}
	}

	tpm.trace = trace;
//	tpm.trace (&trace, start, tmins, tmaxs, end, ps->clientNum, MASK_PLAYERSOLID);

	PM_UpdateLean( ps, cmd, &tpm );
}

/*
================
PM_CheckLadderMove

  Checks to see if we are on a ladder
================
*/
qboolean ladderforward;
vec3_t laddervec;

void PM_CheckLadderMove( void ) {
	vec3_t spot;
	vec3_t flatforward;
	trace_t trace;
	float tracedist;
	#define TRACE_LADDER_DIST   48.0
	qboolean wasOnLadder;

	if ( pm->ps->pm_time ) {
		return;
	}

	//if (pm->ps->pm_flags & PM_DEAD)
	//	return;

	if ( pml.walking ) {
		tracedist = 1.0;
	} else {
		tracedist = TRACE_LADDER_DIST;
	}

	wasOnLadder = ( ( pm->ps->pm_flags & PMF_LADDER ) != 0 );

	pml.ladder = qfalse;
	pm->ps->pm_flags &= ~PMF_LADDER;    // clear ladder bit
	ladderforward = qfalse;

	/*
	if (pm->ps->eFlags & EF_DEAD) {	// dead bodies should fall down ladders
		return;
	}

	if (pm->ps->pm_flags & PM_DEAD && pm->ps->stats[STAT_HEALTH] <= 0)
	{
		return;
	}
	*/
	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
		pm->ps->groundEntityNum = ENTITYNUM_NONE;
		pml.groundPlane = qfalse;
		pml.walking = qfalse;
		return;
	}

	// Can't climb ladders while prone
	if ( pm->ps->eFlags & EF_PRONE ) {
		return;
	}

	// check for ladder
	flatforward[0] = pml.forward[0];
	flatforward[1] = pml.forward[1];
	flatforward[2] = 0;
	VectorNormalize( flatforward );

	VectorMA( pm->ps->origin, tracedist, flatforward, spot );
	pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, spot, pm->ps->clientNum, pm->tracemask );
	if ( ( trace.fraction < 1 ) && ( trace.surfaceFlags & SURF_LADDER ) ) {
		pml.ladder = qtrue;
	}
/*
	if (!pml.ladder && DotProduct(pm->ps->velocity, pml.forward) < 0) {
		// trace along the negative velocity, so we grab onto a ladder if we are trying to reverse onto it from above the ladder
		flatforward[0] = -pm->ps->velocity[0];
		flatforward[1] = -pm->ps->velocity[1];
		flatforward[2] = 0;
		VectorNormalize (flatforward);

		VectorMA (pm->ps->origin, tracedist, flatforward, spot);
		pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, spot, pm->ps->clientNum, pm->tracemask);
		if ((trace.fraction < 1) && (trace.surfaceFlags & SURF_LADDER))
		{
			pml.ladder = qtrue;
		}
	}
*/
	if ( pml.ladder ) {
		VectorCopy( trace.plane.normal, laddervec );
	}

	if ( pml.ladder && !pml.walking && ( trace.fraction * tracedist > 1.0 ) ) {
		vec3_t mins;
		// if we are only just on the ladder, don't do this yet, or it may throw us back off the ladder
		pml.ladder = qfalse;
		VectorCopy( pm->mins, mins );
		mins[2] = -1;
		VectorMA( pm->ps->origin, -tracedist, laddervec, spot );
		pm->trace( &trace, pm->ps->origin, mins, pm->maxs, spot, pm->ps->clientNum, pm->tracemask );
		if ( ( trace.fraction < 1 ) && ( trace.surfaceFlags & SURF_LADDER ) ) {
			ladderforward = qtrue;
			pml.ladder = qtrue;
			pm->ps->pm_flags |= PMF_LADDER; // set ladder bit
		} else {
			pml.ladder = qfalse;
		}
	} else if ( pml.ladder ) {
		pm->ps->pm_flags |= PMF_LADDER; // set ladder bit
	}

	// create some up/down velocity if touching ladder
	if ( pml.ladder ) {
		if ( pml.walking ) {
			// we are currently on the ground, only go up and prevent X/Y if we are pushing forwards
			if ( pm->cmd.forwardmove <= 0 ) {
				pml.ladder = qfalse;
			}
		}
	}

	// if we have just dismounted the ladder at the top, play dismount
	if ( !pml.ladder && wasOnLadder && pm->ps->velocity[2] > 0 ) {
		BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_CLIMB_DISMOUNT, qfalse, qfalse );
	}
	// if we have just mounted the ladder
	if ( pml.ladder && !wasOnLadder && pm->ps->velocity[2] < 0 ) {    // only play anim if going down ladder
		BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_CLIMB_MOUNT, qfalse, qfalse );
	}
}

/*
============
PM_LadderMove
============
*/
void PM_LadderMove( void ) {
	float wishspeed, scale;
	vec3_t wishdir, wishvel;
	float upscale;

	if ( ladderforward ) {
		// move towards the ladder
		VectorScale( laddervec, -200.0, wishvel );
		pm->ps->velocity[0] = wishvel[0];
		pm->ps->velocity[1] = wishvel[1];
	}

	upscale = ( pml.forward[2] + 0.5 ) * 2.5;
	if ( upscale > 1.0 ) {
		upscale = 1.0;
	} else if ( upscale < -1.0 ) {
		upscale = -1.0;
	}

	// forward/right should be horizontal only
	pml.forward[2] = 0;
	pml.right[2] = 0;
	VectorNormalize( pml.forward );
	VectorNormalize( pml.right );

	// move depending on the view, if view is straight forward, then go up
	// if view is down more then X degrees, start going down
	// if they are back pedalling, then go in reverse of above
	scale = PM_CmdScale( &pm->cmd );
	VectorClear( wishvel );

	if ( pm->cmd.forwardmove ) {
		wishvel[2] = 0.9 * upscale * scale * (float)pm->cmd.forwardmove;
	}
//Com_Printf("wishvel[2] = %i, fwdmove = %i\n", (int)wishvel[2], (int)pm->cmd.forwardmove );

	if ( pm->cmd.rightmove ) {
		// strafe, so we can jump off ladder
		vec3_t ladder_right, ang;
		vectoangles( laddervec, ang );
		AngleVectors( ang, NULL, ladder_right, NULL );

		// if we are looking away from the ladder, reverse the right vector
		if ( DotProduct( laddervec, pml.forward ) < 0 ) {
			VectorInverse( ladder_right );
		}

		//VectorMA( wishvel, 0.5 * scale * (float)pm->cmd.rightmove, pml.right, wishvel );
		VectorMA( wishvel, 0.5 * scale * (float)pm->cmd.rightmove, ladder_right, wishvel );
	}

	// do strafe friction
	PM_Friction();

	if ( pm->ps->velocity[0] < 1 && pm->ps->velocity[0] > -1  ) {
		pm->ps->velocity[0] = 0;
	}
	if ( pm->ps->velocity[1] < 1 && pm->ps->velocity[1] > -1 ) {
		pm->ps->velocity[1] = 0;
	}

	wishspeed = VectorNormalize2( wishvel, wishdir );

	PM_Accelerate( wishdir, wishspeed, pm_accelerate );
	if ( !wishvel[2] ) {
		if ( pm->ps->velocity[2] > 0 ) {
			pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
			if ( pm->ps->velocity[2] < 0 ) {
				pm->ps->velocity[2]  = 0;
			}
		} else
		{
			pm->ps->velocity[2] += pm->ps->gravity * pml.frametime;
			if ( pm->ps->velocity[2] > 0 ) {
				pm->ps->velocity[2]  = 0;
			}
		}
	}

//Com_Printf("vel[2] = %i\n", (int)pm->ps->velocity[2] );

	PM_StepSlideMove( qfalse );  // no gravity while going up ladder

	// always point legs forward
	pm->ps->movementDir = 0;
}


/*
==============
PM_Sprint
==============
*/
void PM_Sprint( void ) {
	if ( pm->cmd.buttons & BUTTON_SPRINT && ( pm->cmd.forwardmove || pm->cmd.rightmove ) && !( pm->ps->pm_flags & PMF_DUCKED ) && !( pm->ps->eFlags & EF_PRONE ) ) {
		if ( pm->ps->powerups[PW_ADRENALINE] ) {
			pm->pmext->sprintTime = SPRINTTIME;
		} else if ( pm->ps->powerups[PW_NOFATIGUE] ) {
			// take time from powerup before taking it from sprintTime
			pm->ps->powerups[PW_NOFATIGUE] -= 50;

			// (SA) go ahead and continue to recharge stamina at double
			// rate with stamina powerup even when exerting
			pm->pmext->sprintTime += 10;
			if ( pm->pmext->sprintTime > SPRINTTIME ) {
				pm->pmext->sprintTime = SPRINTTIME;
			}

			if ( pm->ps->powerups[PW_NOFATIGUE] < 0 ) {
				pm->ps->powerups[PW_NOFATIGUE] = 0;
			}
		}
		// JPW NERVE -- sprint time tuned for multiplayer
		else  {
			// JPW NERVE adjusted for framerate independence
			pm->pmext->sprintTime -= 5000 * pml.frametime;
		}
		// jpw

		if ( pm->pmext->sprintTime < 0 ) {
			pm->pmext->sprintTime = 0;
		}

		if ( !pm->ps->sprintExertTime ) {
			pm->ps->sprintExertTime = 1;
		}
	} else
	{
		// JPW NERVE -- in multiplayer, recharge faster for top 75% of sprint bar
		// (for people that *just* use it for jumping, not sprint) this code was
		// mucked about with to eliminate client-side framerate-dependancy in wolf single player
		if ( pm->ps->powerups[PW_ADRENALINE] ) {
			pm->pmext->sprintTime = SPRINTTIME;
		} else if ( pm->ps->powerups[PW_NOFATIGUE] ) { // (SA) recharge at 2x with stamina powerup
			pm->pmext->sprintTime += 10;
		} else {
			int rechargebase = 500;

#ifdef GAMEDLL // Gordon: FIXME: predict leadership clientside
			if ( pm->leadership ) {
				rechargebase = 1000;
			} else
#endif // GAMEDLL
			{
				if ( pm->skill[SK_BATTLE_SENSE] >= 2 ) {
					rechargebase *= 1.6f;
				}
			}

			pm->pmext->sprintTime += rechargebase * pml.frametime;        // JPW NERVE adjusted for framerate independence
			if ( pm->pmext->sprintTime > 5000 ) {
				pm->pmext->sprintTime += rechargebase * pml.frametime;    // JPW NERVE adjusted for framerate independence
			}
			// jpw
		}
		if ( pm->pmext->sprintTime > SPRINTTIME ) {
			pm->pmext->sprintTime = SPRINTTIME;
		}

		pm->ps->sprintExertTime = 0;
	}
}

/*
================
PmoveSingle

================
*/
void trap_SnapVector( float *v );

void PmoveSingle( pmove_t *pmove ) {
	// RF, update conditional values for anim system
	BG_AnimUpdatePlayerStateConditions( pmove );

	pm = pmove;

	// this counter lets us debug movement problems with a journal
	// by setting a conditional breakpoint fot the previous frame
	c_pmove++;

	// clear results
	pm->numtouch = 0;
	pm->watertype = 0;
	pm->waterlevel = 0;

	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
		pm->tracemask &= ~CONTENTS_BODY;    // corpses can fly through bodies
		pm->ps->eFlags &= ~EF_ZOOMING;
	}

	// make sure walking button is clear if they are running, to avoid
	// proxy no-footsteps cheats
	if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) {
		pm->cmd.buttons &= ~BUTTON_WALKING;
	}

	// set the talk balloon flag
	if ( pm->cmd.buttons & BUTTON_TALK ) {
		pm->ps->eFlags |= EF_TALK;
	} else {
		pm->ps->eFlags &= ~EF_TALK;
	}

	// set the firing flag for continuous beam weapons

	pm->ps->eFlags &= ~( EF_FIRING | EF_ZOOMING );

	if ( pm->cmd.wbuttons & WBUTTON_ZOOM && pm->ps->stats[STAT_HEALTH] >= 0 && !( pm->ps->weaponDelay ) ) {
		if ( pm->ps->stats[STAT_KEYS] & ( 1 << INV_BINOCS ) ) {    // (SA) binoculars are an inventory item (inventory==keys)
			if ( !BG_IsScopedWeapon( pm->ps->weapon ) &&       // don't allow binocs if using the sniper scope
				 !BG_PlayerMounted( pm->ps->eFlags ) &&     // or if mounted on a weapon
				 // rain - #215 - don't allow binocs w/ mounted mob. MG42 or mortar either.
				 pm->ps->weapon != WP_MOBILE_MG42_SET &&
				 pm->ps->weapon != WP_MORTAR_SET ) {

				pm->ps->eFlags |= EF_ZOOMING;
			}
		}

		// don't allow binocs if in the middle of throwing grenade
		if ( ( pm->ps->weapon == WP_GRENADE_LAUNCHER || pm->ps->weapon == WP_GRENADE_PINEAPPLE || pm->ps->weapon == WP_DYNAMITE ) && pm->ps->grenadeTimeLeft > 0 ) {
			pm->ps->eFlags &= ~EF_ZOOMING;
		}
	}


	if ( !( pm->ps->pm_flags & PMF_RESPAWNED ) &&
		 ( pm->ps->pm_type != PM_INTERMISSION ) ) {

		// check for ammo
		if ( PM_WeaponAmmoAvailable( pm->ps->weapon ) ) {
			// check if zooming
			// DHM - Nerve :: Let's use the same flag we just checked above, Ok?
			if ( !( pm->ps->eFlags & EF_ZOOMING ) ) {
				if ( !pm->ps->leanf ) {
					if ( pm->ps->weaponstate == WEAPON_READY || pm->ps->weaponstate == WEAPON_FIRING ) {

						// all clear, fire!
						if ( pm->cmd.buttons & BUTTON_ATTACK && !( pm->cmd.buttons & BUTTON_TALK ) ) {
							pm->ps->eFlags |= EF_FIRING;
						}
					}
				}
			}
		}
	}


	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
		if ( pm->ps->stats[STAT_PLAYER_CLASS] == PC_COVERTOPS ) {
			pm->pmext->silencedSideArm |= 1;
		}
	}

	// clear the respawned flag if attack and use are cleared
	if ( pm->ps->stats[STAT_HEALTH] > 0 &&
		 !( pm->cmd.buttons & ( BUTTON_ATTACK /*| BUTTON_USE_HOLDABLE*/ ) ) ) {
		pm->ps->pm_flags &= ~PMF_RESPAWNED;
	}

	// if talk button is down, dissallow all other input
	// this is to prevent any possible intercept proxy from
	// adding fake talk balloons
	if ( pmove->cmd.buttons & BUTTON_TALK ) {
		// keep the talk button set tho for when the cmd.serverTime > 66 msec
		// and the same cmd is used multiple times in Pmove
		pmove->cmd.buttons = BUTTON_TALK;
		pmove->cmd.wbuttons = 0;
		pmove->cmd.forwardmove = 0;
		pmove->cmd.rightmove = 0;
		pmove->cmd.upmove = 0;
		pmove->cmd.doubleTap = 0;
	}

	// fretn, moved from engine to this very place
	// no movement while using a static mg42
	if ( pm->ps->persistant[PERS_HWEAPON_USE] ) {
		pmove->cmd.forwardmove = 0;
		pmove->cmd.rightmove = 0;
		pmove->cmd.upmove = 0;
	}

	// clear all pmove local vars
	memset( &pml, 0, sizeof( pml ) );

	// determine the time
	pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
	if ( pml.msec < 1 ) {
		pml.msec = 1;
	} else if ( pml.msec > 200 ) {
		pml.msec = 200;
	}
	pm->ps->commandTime = pmove->cmd.serverTime;

	// save old org in case we get stuck
	VectorCopy( pm->ps->origin, pml.previous_origin );

	// save old velocity for crashlanding
	VectorCopy( pm->ps->velocity, pml.previous_velocity );

	pml.frametime = pml.msec * 0.001;

	// update the viewangles
	if ( pm->ps->pm_type != PM_FREEZE ) { // Arnout: added PM_FREEZE
		if ( !( pm->ps->pm_flags & PMF_LIMBO ) ) { // JPW NERVE
			// rain - added tracemask
			PM_UpdateViewAngles( pm->ps, pm->pmext, &pm->cmd, pm->trace, pm->tracemask );   //----(SA)	modified

		}
	}
	AngleVectors( pm->ps->viewangles, pml.forward, pml.right, pml.up );

	if ( pm->cmd.upmove < 10 ) {
		// not holding jump
		pm->ps->pm_flags &= ~PMF_JUMP_HELD;
	}

	// decide if backpedaling animations should be used
	if ( pm->cmd.forwardmove < 0 ) {
		pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
	} else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
		pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
	}

	if ( pm->ps->pm_type >= PM_DEAD || pm->ps->pm_flags & ( PMF_LIMBO | PMF_TIME_LOCKPLAYER ) ) {           // DHM - Nerve
		pm->cmd.forwardmove = 0;
		pm->cmd.rightmove = 0;
		pm->cmd.upmove = 0;
	}

	if ( pm->ps->pm_type == PM_SPECTATOR ) {
		PM_CheckDuck();
		PM_FlyMove();
		PM_DropTimers();
		return;
	}

	if ( pm->ps->pm_type == PM_NOCLIP ) {
		PM_NoclipMove();
		PM_DropTimers();
		return;
	}

	if ( pm->ps->pm_type == PM_FREEZE ) {
		return;     // no movement at all
	}

	if ( pm->ps->pm_type == PM_INTERMISSION ) {
		return;     // no movement at all
	}

	// ydnar: need gravity etc to affect a player with a set mortar
	if ( pm->ps->weapon == WP_MORTAR_SET && pm->ps->pm_type == PM_NORMAL ) {
		pm->cmd.forwardmove = 0;
		pm->cmd.rightmove = 0;
		pm->cmd.upmove = 0;

		#if 0
		VectorClear( pm->ps->velocity );

		// set watertype, and waterlevel
		PM_SetWaterLevel();
		pml.previous_waterlevel = pmove->waterlevel;

		// set mins, maxs, and viewheight
		PM_CheckDuck();

		// set groundentity
		PM_GroundTrace();

		// handle movement (gravity)
		{
			vec3_t endVelocity;
			VectorCopy( pm->ps->velocity, endVelocity );
			endVelocity[2] -= pm->ps->gravity * pml.frametime;
			pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5;
			if ( pml.groundPlane ) {
				PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP );
			}
		}

		PM_Weapon();

		BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_IDLE, qtrue );

		return;
		#endif
	}

	// set watertype, and waterlevel
	PM_SetWaterLevel();
	pml.previous_waterlevel = pmove->waterlevel;

	// set mins, maxs, and viewheight
	//if( !PM_CheckProne() ) {
//		PM_CheckDuck ();
	//}
	if ( !PM_CheckProne() ) {
		PM_CheckDuck();
	}

	// set groundentity
	PM_GroundTrace();

	/*if( pm->ps->eFlags & EF_PRONE && !pml.walking ) {
	// this is the one we were using
		// can't be prone in midair
		pm->ps->eFlags &= ~EF_PRONE;
		pm->ps->eFlags &= ~EF_PRONE_MOVING;
		pm->pmext->proneTime = -pm->cmd.serverTime;	// timestamp 'stop prone'

		if( pm->ps->weapon == WP_MOBILE_MG42_SET ) {
			PM_BeginWeaponChange( WP_MOBILE_MG42_SET, WP_MOBILE_MG42, qfalse );
		}
	}*/

	if ( pm->ps->pm_type == PM_DEAD ) {
		PM_DeadMove();

		if ( pm->ps->weapon == WP_MORTAR_SET ) {
			pm->ps->weapon = WP_MORTAR;
		}
	} else {
		if ( pm->ps->weapon == WP_MOBILE_MG42_SET ) {
			if ( !( pm->ps->eFlags & EF_PRONE ) ) {
				PM_BeginWeaponChange( WP_MOBILE_MG42_SET, WP_MOBILE_MG42, qfalse );
#ifdef CGAMEDLL
				cg.weaponSelect = WP_MOBILE_MG42;
#endif // CGAMEDLL
			}
		}
		if ( pm->ps->weapon == WP_SATCHEL_DET ) {
			if ( !( pm->ps->ammoclip[ WP_SATCHEL_DET ] ) ) {
				PM_BeginWeaponChange( WP_SATCHEL_DET, WP_SATCHEL, qtrue );
#ifdef CGAMEDLL
				cg.weaponSelect = WP_SATCHEL;
#endif // CGAMEDLL
			}
		}
	}

	// Ridah, ladders
	PM_CheckLadderMove();

	PM_DropTimers();

	if ( pml.ladder ) {
		PM_LadderMove();
	} else if ( pm->ps->pm_flags & PMF_TIME_WATERJUMP ) {
		PM_WaterJumpMove();
	} else if ( pm->waterlevel > 1 ) {
		// swimming
		PM_WaterMove();
	} else if ( pml.walking && !( pm->ps->eFlags & EF_MOUNTEDTANK ) ) {
		// walking on ground
		PM_WalkMove();
	} else if ( !( pm->ps->eFlags & EF_MOUNTEDTANK ) ) {
		// airborne
		PM_AirMove();
	}

	if ( pm->ps->eFlags & EF_MOUNTEDTANK ) {
		VectorClear( pm->ps->velocity );

		pm->ps->viewheight = DEFAULT_VIEWHEIGHT;

		BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_IDLE, qtrue );
	}


	/*if( pm->ps->eFlags & EF_PRONE && !pml.walking ) {
		// can't be prone in midair
		pm->ps->eFlags &= ~EF_PRONE;
		pm->ps->eFlags &= ~EF_PRONE_MOVING;
		pm->pmext->proneTime = -pm->cmd.serverTime;	// timestamp 'stop prone'

		if( pm->ps->weapon == WP_MOBILE_MG42_SET ) {
			PM_BeginWeaponChange( WP_MOBILE_MG42_SET, WP_MOBILE_MG42, qfalse );
		}
	}*/

	PM_Sprint();

	// set groundentity, watertype, and waterlevel
	PM_GroundTrace();
	PM_SetWaterLevel();

	// weapons
	PM_Weapon();

	// footstep events / legs animations
	PM_Footsteps();

	// entering / leaving water splashes
	PM_WaterEvents();

	// snap some parts of playerstate to save network bandwidth
	trap_SnapVector( pm->ps->velocity );
//	SnapVector( pm->ps->velocity );
}


/*
================
Pmove

Can be called by either the server or the client
================
*/
int Pmove( pmove_t *pmove ) {
	int finalTime;

	// Ridah
/*	if (pmove->ps->eFlags & EF_DUMMY_PMOVE) {
		PmoveSingle( pmove );
		return (0);
	}*/
	// done.

	finalTime = pmove->cmd.serverTime;

	if ( finalTime < pmove->ps->commandTime ) {
		return ( 0 ); // should not happen
	}

	if ( finalTime > pmove->ps->commandTime + 1000 ) {
		pmove->ps->commandTime = finalTime - 1000;
	}

	// after a loadgame, prevent huge pmove's
	if ( pmove->ps->pm_flags & PMF_TIME_LOAD ) {
		if ( finalTime - pmove->ps->commandTime > 50 ) {
			pmove->ps->commandTime = finalTime - 50;
		}
	}

	pmove->ps->pmove_framecount = ( pmove->ps->pmove_framecount + 1 ) & ( ( 1 << PS_PMOVEFRAMECOUNTBITS ) - 1 );

	// RF
	pm = pmove;
	PM_AdjustAimSpreadScale();

//	startedTorsoAnim = -1;
//	startedLegAnim = -1;

	// chop the move up if it is too long, to prevent framerate
	// dependent behavior
	while ( pmove->ps->commandTime != finalTime ) {
		int msec;

		msec = finalTime - pmove->ps->commandTime;

		if ( pmove->pmove_fixed ) {
			if ( msec > pmove->pmove_msec ) {
				msec = pmove->pmove_msec;
			}
		} else {
			// rain - this was 66 (15fps), but I've changed it to
			// 50 (20fps, max rate of mg42) to alleviate some of the
			// framerate dependency with the mg42.
			// in reality, this should be split according to sv_fps,
			// and pmove() shouldn't handle weapon firing
			if ( msec > 50 ) {
				msec = 50;
			}
		}
		pmove->cmd.serverTime = pmove->ps->commandTime + msec;
		PmoveSingle( pmove );

		if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) {
			pmove->cmd.upmove = 20;
		}
	}

	// rain - sanity check weapon heat
	if ( pmove->ps->curWeapHeat > 255 ) {
		pmove->ps->curWeapHeat = 255;
	} else if ( pmove->ps->curWeapHeat < 0 ) {
		pmove->ps->curWeapHeat = 0;
	}

	//PM_CheckStuck();

	if ( ( pm->ps->stats[STAT_HEALTH] <= 0 || pm->ps->pm_type == PM_DEAD ) && pml.groundTrace.surfaceFlags & SURF_MONSTERSLICK ) {
		return ( pml.groundTrace.surfaceFlags );
	} else {
		return ( 0 );
	}

}
